use super::*;
#[test]
fn openai_surface_probe_skips_plain_http_multiplexers() {
assert!(!should_probe_openai_surface(
newt_core::BackendKind::Openai,
true,
"model",
"http://gpu-box:8000",
Some(newt_core::Serving::Multiplexer),
));
assert!(should_probe_openai_surface(
newt_core::BackendKind::Openai,
true,
"model",
"http://127.0.0.1:8000",
Some(newt_core::Serving::Instance),
));
assert!(should_probe_openai_surface(
newt_core::BackendKind::Openai,
true,
"model",
"https://inference.example.test",
Some(newt_core::Serving::Multiplexer),
));
}
#[test]
fn attribution_ledger_uses_resolved_identity_email() {
let id = newt_core::AgentIdentity::default();
let mut ledger = newt_core::attribution::AttributionLedger::new(id.email.clone());
ledger.record(
"nemotron-3-nano:30b",
"newt-agent",
newt_core::build_info::PACKAGE_VERSION,
);
assert_eq!(
ledger.render(),
format!(
"Co-authored-by: nemotron-3-nano:30b (v{} {}) <309460085+newt-agent@users.noreply.github.com>",
newt_core::build_info::PACKAGE_VERSION,
newt_core::build_info::SOURCE_ID
)
);
assert!(ledger.render().contains(newt_core::DEFAULT_AGENT_EMAIL));
assert!(
!ledger.render().contains("noreply@newt-agent.com"),
"old wrong email is gone"
);
assert!(
!ledger.render().contains("[bot]"),
"default attribution is the User account, not the App bot"
);
let custom = newt_core::AgentIdentity {
name: "my-agent".into(),
email: "my-agent@example.com".into(),
..newt_core::AgentIdentity::default()
};
let mut custom_ledger = newt_core::attribution::AttributionLedger::new(custom.email.clone());
custom_ledger.record(
"ornith:35b",
"newt-agent",
newt_core::build_info::PACKAGE_VERSION,
);
assert_eq!(
custom_ledger.render(),
format!(
"Co-authored-by: ornith:35b (newt-agent v{} {}) <my-agent@example.com>",
newt_core::build_info::PACKAGE_VERSION,
newt_core::build_info::SOURCE_ID
)
);
}
#[test]
fn runtime_context_block_reports_identity_without_a_special_git_dialect() {
let id = newt_core::AgentIdentity::default();
let blk = runtime_context_block(
"m",
"http://h",
newt_core::BackendKind::Ollama,
&id,
newt_core::agentic::PromptDisposition::Act,
);
assert!(blk.contains("newt-agent <309460085+newt-agent@users.noreply.github.com>"));
assert!(!blk.contains("Prefer the `git` tool"));
assert!(!blk.contains("op=amend"));
assert!(!blk.contains("git -c user.name="));
let custom = newt_core::AgentIdentity {
name: "custom".into(),
email: "custom@example.com".into(),
..newt_core::AgentIdentity::default()
};
let custom_blk = runtime_context_block(
"m",
"http://h",
newt_core::BackendKind::Ollama,
&custom,
newt_core::agentic::PromptDisposition::Act,
);
assert!(custom_blk.contains("custom <custom@example.com>"));
}
#[test]
fn workspace_state_block_formats_dirty_snapshot_with_timestamp() {
let block = format_workspace_state_block(&WorkspaceStateSnapshot {
timestamp: "2026-07-04T21:23:45-04:00".to_string(),
branch: Some("feat/help-rollups".to_string()),
dirty_files: vec![
"newt-tui/src/help_sections.rs".to_string(),
"newt-tui/src/lib.rs".to_string(),
],
git_status_available: true,
});
assert!(block.starts_with("<workspace_state>\ntimestamp: 2026-07-04T21:23:45-04:00"));
assert!(block.contains("branch: feat/help-rollups"), "{block}");
assert!(block.contains("dirty files (2):"), "{block}");
assert!(
block.contains("- newt-tui/src/help_sections.rs")
&& block.contains("- newt-tui/src/lib.rs"),
"{block}"
);
assert!(
block.contains("unlanded local changes exist; do not treat them as upstream-complete work"),
"{block}"
);
assert!(
block.contains("next completion step: verify, commit, push/open PR, or state blocker"),
"{block}"
);
}
#[test]
fn workspace_state_block_formats_clean_snapshot_without_dirty_nudge() {
let block = format_workspace_state_block(&WorkspaceStateSnapshot {
timestamp: "2026-07-04T21:23:45-04:00".to_string(),
branch: Some("main".to_string()),
dirty_files: Vec::new(),
git_status_available: true,
});
assert!(block.contains("timestamp: 2026-07-04T21:23:45-04:00"));
assert!(block.contains("branch: main"), "{block}");
assert!(block.contains("dirty files: none"), "{block}");
assert!(block.contains("local changes: clean"), "{block}");
assert!(!block.contains("unlanded local changes exist"), "{block}");
}
#[test]
fn parse_git_porcelain_dirty_files_dedupes_and_tracks_rename_target() {
let files = parse_git_porcelain_dirty_files(
" M newt-tui/src/lib.rs\n\
?? docs/new file.md\n\
R old.rs -> src/new.rs\n\
M newt-tui/src/lib.rs\n",
);
assert_eq!(
files,
vec![
"newt-tui/src/lib.rs".to_string(),
"docs/new file.md".to_string(),
"src/new.rs".to_string(),
]
);
}
#[test]
fn bang_command_strips_and_trims_the_escape() {
assert_eq!(bang_command("!date"), Some("date"));
assert_eq!(bang_command("! date"), Some("date"));
assert_eq!(bang_command("! pa login "), Some("pa login"));
assert_eq!(bang_command("! echo hi | wc -c"), Some("echo hi | wc -c"));
}
#[test]
fn management_login_argv_is_literal_and_never_shell_expanded() {
let argv = vec![
"document-client".to_owned(),
"login".to_owned(),
"literal; argument $(no-expansion)".to_owned(),
];
let command = operator_argv_command(&argv).unwrap();
assert_eq!(command.get_program(), "document-client");
assert_eq!(
command.get_args().collect::<Vec<_>>(),
vec!["login", "literal; argument $(no-expansion)"]
);
assert!(operator_argv_command(&[]).is_err());
assert!(operator_argv_command(&[String::new()]).is_err());
}
#[test]
fn bang_command_ignores_non_bang_and_bare_bang() {
assert_eq!(bang_command("date"), None, "no leading bang");
assert_eq!(bang_command("/help"), None, "slash is not a bang");
assert_eq!(bang_command("!"), None, "bare bang has no command");
assert_eq!(bang_command("! "), None, "whitespace-only is empty");
assert_eq!(
bang_command("the ! is mid-line"),
None,
"bang must lead the line"
);
}
#[test]
#[cfg(not(windows))]
fn bang_shell_is_a_noninteractive_unix_shell() {
let (shell, flag) = bang_shell();
assert_eq!(flag, "-c");
assert!(!shell.is_empty(), "a shell is always resolved");
}
#[test]
#[cfg(not(windows))]
fn run_bang_escape_returns_on_success() {
run_bang_escape("true", false, false);
}
#[test]
#[cfg(not(windows))]
fn run_bang_escape_returns_on_nonzero_exit() {
run_bang_escape("exit 3", false, false);
}
#[serial_test::serial(real_fs)]
#[test]
#[cfg(windows)]
fn bang_shell_is_a_windows_shell_with_slash_c() {
let (shell, flag) = bang_shell();
assert_eq!(flag, "/C");
assert!(!shell.is_empty(), "a shell is always resolved");
}
fn write_pyo3_binding(root: &std::path::Path, krate: &str, submodule: &str) {
let dir = root.join(krate).join("src");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(
dir.join("pyo3_module.rs"),
format!("#[pyclass(name=\"X\", module=\"newt_agent._newt_agent.{submodule}\")] struct X;"),
)
.unwrap();
}
#[serial_test::serial(real_fs)]
#[test]
fn verify_gate_summary_flags_fabrication_and_passes_clean() {
use newt_core::verify_gate::SurfaceMatch;
let dir = tempfile::tempdir().unwrap();
write_pyo3_binding(dir.path(), "newt-core", "core");
let ws = dir.path().to_str().unwrap();
std::fs::create_dir_all(dir.path().join("examples")).unwrap();
std::fs::write(dir.path().join("examples/bad.py"), "import newt_core\n").unwrap();
let warn = verify_gate_summary(ws, SurfaceMatch::Exact).expect("a fabrication warning");
assert!(
warn.contains("verify_gate") && warn.contains("newt_core"),
"{warn}"
);
std::fs::remove_file(dir.path().join("examples/bad.py")).unwrap();
std::fs::write(
dir.path().join("examples/ok.py"),
"from newt_agent._newt_agent.core import X\nimport os\n",
)
.unwrap();
assert!(verify_gate_summary(ws, SurfaceMatch::Exact).is_none());
}
#[serial_test::serial(real_fs)]
#[test]
fn verify_gate_summary_noops_without_pyo3_surface() {
use newt_core::verify_gate::SurfaceMatch;
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("thing.py"), "import newt_core\n").unwrap();
assert!(verify_gate_summary(dir.path().to_str().unwrap(), SurfaceMatch::Exact).is_none());
}
#[serial_test::serial(real_fs)]
#[tokio::test]
async fn retry_revert_undoes_only_newts_writes() {
use newt_core::verify_gate::{SurfaceMatch, WriteLedger};
let dir = tempfile::tempdir().unwrap();
write_pyo3_binding(dir.path(), "newt-core", "core");
let ws = dir.path().to_str().unwrap();
std::fs::create_dir_all(dir.path().join("examples")).unwrap();
let edited = dir.path().join("examples/edited.py");
std::fs::write(&edited, "from newt_agent._newt_agent.core import X\n").unwrap();
let untouched = dir.path().join("examples/untouched.py");
std::fs::write(&untouched, "import newt_core\n").unwrap();
let ledger = std::cell::RefCell::new(WriteLedger::new());
ledger.borrow_mut().note_before_write(&edited);
std::fs::write(&edited, "import newt_core\n").unwrap(); let created = dir.path().join("examples/new.py");
ledger.borrow_mut().note_before_write(&created);
std::fs::write(&created, "import newt_coder\n").unwrap();
let action = retry_revert(ws, SurfaceMatch::Exact, &ledger)
.await
.expect("a revert action");
assert!(
action.banner.contains("retry: reverted"),
"{}",
action.banner
);
assert!(
action.corrective.contains("newt_core") || action.corrective.contains("newt_coder"),
"corrective names the bad import: {}",
action.corrective
);
assert!(
action.corrective.contains("newt_agent._newt_agent.core"),
"corrective carries the authoritative surface"
);
assert_eq!(
std::fs::read_to_string(&edited).unwrap(),
"from newt_agent._newt_agent.core import X\n"
);
assert!(!created.exists(), "newt's created fabrication is deleted");
assert_eq!(
std::fs::read_to_string(&untouched).unwrap(),
"import newt_core\n",
"a file newt did not write must never be reverted or deleted"
);
assert!(
retry_revert(ws, SurfaceMatch::Exact, &ledger)
.await
.is_none(),
"nothing newt wrote remains flagged"
);
}
#[test]
fn retry_step_reprompts_until_the_budget_is_spent() {
assert_eq!(retry_step(2), RetryStep::Reprompt);
assert_eq!(retry_step(1), RetryStep::Reprompt);
assert_eq!(retry_step(0), RetryStep::GiveUp);
}
#[test]
fn inline_header_color_contains_brand_and_ready_lines() {
let s = render_inline_header("/w", true);
assert!(s.contains("New Tech Agent"));
assert!(s.contains(&format!("v{VERSION}")));
assert!(s.contains("ready — type a task, /help for commands, /exit to quit"));
assert!(s.contains("\x1b[23G"));
}
#[test]
fn inline_header_plain_names_version_and_workspace() {
let s = render_inline_header("/some/workspace", false);
assert!(s.contains(&format!("newt v{VERSION}")));
assert!(s.contains("/some/workspace"));
assert!(!s.contains('\x1b'));
}
#[test]
fn inline_header_lists_plugins_when_brand_set() {
let _g = crate::test_env_guard::env_write_guard();
unsafe { std::env::set_var("NEWT_BRAND_PLUGINS", "mogul, diagram") };
let color = render_inline_header("/w", true);
let plain = render_inline_header("/w", false);
unsafe { std::env::remove_var("NEWT_BRAND_PLUGINS") };
assert!(color.contains("plugins: mogul, diagram"));
assert!(plain.contains("plugins: mogul, diagram"));
assert!(!render_inline_header("/w", false).contains("plugins:"));
}
#[test]
fn inline_header_ends_with_blank_line_before_chat() {
for color in [true, false] {
let s = render_inline_header("/w", color);
assert!(s.ends_with("\n\n"), "header must end with a blank line");
}
}
#[test]
fn resolve_workspace_falls_back_gracefully() {
let p = std::path::Path::new("/some/workspace");
assert_eq!(resolve_workspace(Some(p)), "/some/workspace");
}
#[test]
fn slash_exit_returns_false() {
for cmd in ["/exit", "/quit"] {
let result = dispatch_slash(cmd, "/ws", false, false, false).unwrap();
assert!(!result, "{cmd} should return false (exit)");
}
}
#[test]
fn config_helpers_read_from_passed_config_not_disk() {
let cfg = newt_core::Config {
tui: Some(newt_core::TuiConfig {
max_tool_rounds: 7,
workflow_grace_rounds: 4,
tool_output_lines: 3,
..Default::default()
}),
..Default::default()
};
assert_eq!(max_tool_rounds(&cfg), 7);
assert_eq!(workflow_grace_rounds(&cfg), 4);
assert_eq!(tool_output_lines(&cfg), 3);
assert_eq!(resolve_tui(&cfg).map(|t| t.max_tool_rounds), Some(7));
assert_eq!(resolve_tui(&cfg).map(|t| t.workflow_grace_rounds), Some(4));
let empty = newt_core::Config::default();
assert_eq!(max_tool_rounds(&empty), 40);
assert_eq!(workflow_grace_rounds(&empty), 5);
assert_eq!(tool_output_lines(&empty), 20);
assert_eq!(resolve_tui(&empty), None);
}
#[test]
fn narration_intent_line_is_absent_by_default_and_present_when_enabled() {
assert_eq!(narration_intent_line(false), None);
let line = narration_intent_line(true).expect("enabled appends the sentence");
assert!(line.contains("Before each tool call"));
assert!(line.contains("in the same reply"));
}
#[test]
fn tool_round_limit_commands_parse_expected_forms() {
assert_eq!(
parse_tool_round_limit_command("/rounds").unwrap(),
ToolRoundLimitCommand::Show
);
assert_eq!(
parse_tool_round_limit_command("/rounds status").unwrap(),
ToolRoundLimitCommand::Show
);
assert_eq!(
parse_tool_round_limit_command("/tool-rounds 50").unwrap(),
ToolRoundLimitCommand::Set(50)
);
assert_eq!(
parse_tool_round_limit_command("/max-rounds double").unwrap(),
ToolRoundLimitCommand::Double
);
assert_eq!(
parse_tool_round_limit_command("/rounds x2").unwrap(),
ToolRoundLimitCommand::Double
);
assert_eq!(
parse_tool_round_limit_command("/rounds reset").unwrap(),
ToolRoundLimitCommand::Reset
);
assert_eq!(
parse_tool_round_limit_command("/rounds default").unwrap(),
ToolRoundLimitCommand::Reset
);
assert_eq!(
parse_tool_round_limit_command("/rounds auto").unwrap(),
ToolRoundLimitCommand::Reset
);
assert_eq!(
parse_tool_round_limit_command("/rounds config").unwrap(),
ToolRoundLimitCommand::Configured
);
assert_eq!(
parse_tool_round_limit_command("/rounds unlimited").unwrap(),
ToolRoundLimitCommand::Unlimited
);
assert_eq!(
parse_tool_round_limit_command("/rounds run until finished").unwrap(),
ToolRoundLimitCommand::Unlimited
);
assert!(parse_tool_round_limit_command("/roundsx 10").is_err());
assert!(parse_tool_round_limit_command("/rounds 0").is_err());
assert!(parse_tool_round_limit_command("/rounds 10001").is_err());
assert!(parse_tool_round_limit_command("/rounds many").is_err());
}
#[test]
fn the_typed_rounds_verb_survives_the_parse() {
for (input, verb, arg) in [
("/rounds 320", "rounds", "320"),
("/tool-rounds 50", "tool-rounds", "50"),
("/max-rounds double", "max-rounds", "double"),
("/rounds", "rounds", ""),
] {
assert_eq!(
tool_round_limit_command(input),
Some((verb, arg)),
"`{input}` lost the verb or the argument"
);
}
assert_eq!(tool_round_limit_command("/roundsx 10"), None);
assert_eq!(tool_round_limit_command("/round 10"), None);
}
#[test]
fn tool_round_limit_override_resolves_and_reports() {
use newt_core::Tenacity;
use newt_core::tenacity::ToolRoundLimitSource;
let plain = effective_tool_round_limit(25, None, None);
assert_eq!(
(plain.rounds, plain.source),
(25, ToolRoundLimitSource::Config)
);
assert!(!plain.is_escalated());
let standard = effective_tool_round_limit(25, Some(Tenacity::Normal), None);
assert_eq!(
(standard.rounds, standard.source),
(25, ToolRoundLimitSource::Config),
"a tenacity level that does not RAISE the limit did not decide it"
);
let relentless = effective_tool_round_limit(25, Some(Tenacity::Relentless), None);
assert_eq!(
(relentless.rounds, relentless.source, relentless.configured),
(
EFFECTIVELY_UNLIMITED_TOOL_ROUNDS,
ToolRoundLimitSource::Tenacity,
25
),
"an explicitly selected relentless posture should not hit the ordinary safety cap"
);
assert!(relentless.is_escalated());
assert_eq!(relentless.tenacity, Some(Tenacity::Relentless.into()));
let overridden = effective_tool_round_limit(25, Some(Tenacity::Relentless), Some(7));
assert_eq!(
(overridden.rounds, overridden.source, overridden.configured),
(7, ToolRoundLimitSource::Override, 25),
"a deliberate /rounds override remains the outermost limit"
);
let raised = effective_tool_round_limit(25, None, Some(50));
assert_eq!(
(raised.rounds, raised.source, raised.configured),
(50, ToolRoundLimitSource::Override, 25),
"the escalation this issue is about: 50 enforced over a configured 25"
);
assert_eq!(double_tool_round_limit(25), 50);
assert_eq!(
double_tool_round_limit(EFFECTIVELY_UNLIMITED_TOOL_ROUNDS),
EFFECTIVELY_UNLIMITED_TOOL_ROUNDS
);
assert_eq!(
double_tool_round_limit(EFFECTIVELY_UNLIMITED_TOOL_ROUNDS * 2),
EFFECTIVELY_UNLIMITED_TOOL_ROUNDS * 2,
"doubling must never lower a larger configured/session value"
);
assert_eq!(
tool_round_limit_status(25, None, None),
"initial tool-call round allowance: 25 (config/model default)"
);
assert_eq!(
tool_round_limit_status(25, None, Some(50)),
"initial tool-call round allowance: 50 this session (config/model default 25)"
);
assert_eq!(
tool_round_limit_status(25, Some(Tenacity::Relentless), None),
"initial tool-call round allowance: 10000 (effectively unlimited; explicit relentless tenacity; config/model default 25)"
);
assert_eq!(
tool_round_limit_status(25, Some(Tenacity::Relentless), Some(7)),
"initial tool-call round allowance: 7 this session (explicit relentless tenacity default 10000 (effectively unlimited); config/model default 25)"
);
assert!(
tool_round_limit_status(25, None, Some(EFFECTIVELY_UNLIMITED_TOOL_ROUNDS))
.contains("effectively unlimited")
);
let relentless = Some(Tenacity::Relentless);
assert_eq!(
apply_tool_round_limit_command(25, relentless, None, ToolRoundLimitCommand::Configured,),
Some(25),
"config explicitly bypasses the tenacity-derived default"
);
assert_eq!(
apply_tool_round_limit_command(25, relentless, Some(25), ToolRoundLimitCommand::Reset,),
None,
"reset returns to automatic derivation"
);
assert_eq!(
effective_tool_round_limit(
25,
relentless,
apply_tool_round_limit_command(25, relentless, Some(25), ToolRoundLimitCommand::Reset,),
)
.rounds,
EFFECTIVELY_UNLIMITED_TOOL_ROUNDS
);
assert_eq!(
apply_tool_round_limit_command(20_000, relentless, None, ToolRoundLimitCommand::Unlimited,),
Some(20_000),
"unlimited must not lower an already larger effective limit"
);
assert!(
tool_round_limit_status(20_000, relentless, None).contains("explicit relentless tenacity"),
"status keeps operator provenance even when config already exceeds the sentinel"
);
}
#[test]
fn psyche_apply_summary_includes_the_effective_round_source() {
use newt_core::Tenacity;
let summary = psyche_apply_summary(
"psyche: meticulous + relentless",
40,
Some(Tenacity::Relentless),
Some(7),
);
assert!(
summary.starts_with("psyche: meticulous + relentless"),
"{summary}"
);
assert!(
summary.contains("initial tool-call round allowance: 7 this session"),
"{summary}"
);
assert!(
summary.contains("relentless tenacity default 10000"),
"{summary}"
);
}
#[test]
fn spill_commands_parse_expected_forms() {
assert_eq!(parse_spill_command("/spill").unwrap(), SpillCommand::Status);
assert_eq!(
parse_spill_command("/spill status").unwrap(),
SpillCommand::Status
);
assert_eq!(
parse_spill_command("/spill 7").unwrap(),
SpillCommand::Set(7)
);
assert_eq!(
parse_spill_command("/spill 0").unwrap(),
SpillCommand::Set(0)
);
assert_eq!(
parse_spill_command("/spill reset").unwrap(),
SpillCommand::Reset
);
assert_eq!(
parse_spill_command("/spill summary").unwrap(),
SpillCommand::Summary
);
assert_eq!(
parse_spill_command("/spill excerpt").unwrap(),
SpillCommand::Excerpt
);
assert_eq!(
parse_spill_command("/spill last").unwrap(),
SpillCommand::Last
);
assert_eq!(
parse_spill_command("/spill open 17").unwrap(),
SpillCommand::Open(17)
);
assert!(parse_spill_command("/spill open").is_err());
assert!(parse_spill_command("/spill open 0").is_err());
assert!(parse_spill_command("/spill open 3 extra").is_err());
assert!(parse_spill_command("/spillage 7").is_err());
assert!(parse_spill_command("/spill many").is_err());
}
#[test]
fn spill_summary_defaults_to_the_surface_and_override_wins() {
assert!(effective_spill_summary(true, None), "rich collapses");
assert!(
!effective_spill_summary(false, None),
"lean never collapses"
);
assert!(
!effective_spill_summary(true, Some(false)),
"/spill excerpt"
);
assert!(effective_spill_summary(false, Some(true)), "/spill summary");
let on = spill_status(3, None, true, true, SpillEligibility::Available);
assert!(
on.contains("results collapse to a summary line (/spill excerpt restores rows)"),
"{on}"
);
let off = spill_status(3, None, false, true, SpillEligibility::Available);
assert!(!off.contains("summary line"), "{off}");
}
#[test]
fn spill_status_never_claims_a_collapse_that_cannot_engage() {
let lean = spill_status(3, None, true, false, SpillEligibility::Available);
assert!(!lean.contains("summary line"), "{lean}");
assert!(
lean.contains("committed results always print in full"),
"{lean}"
);
let unbounded = spill_status(3, Some(0), true, true, SpillEligibility::Available);
assert!(!unbounded.contains("summary line"), "{unbounded}");
let rich = spill_status(3, None, true, true, SpillEligibility::Available);
assert!(rich.contains("summary line"), "{rich}");
assert!(!rich.contains("print in full"), "{rich}");
}
#[test]
fn spill_reset_clears_both_session_knobs() {
let mut lines = Some(7usize);
let mut summary = Some(true);
apply_spill_command(SpillCommand::Reset, &mut lines, &mut summary);
assert_eq!((lines, summary), (None, None), "Reset clears BOTH");
apply_spill_command(SpillCommand::Set(5), &mut lines, &mut summary);
assert_eq!(
(lines, summary),
(Some(5), None),
"Set leaves the mode alone"
);
apply_spill_command(SpillCommand::Summary, &mut lines, &mut summary);
assert_eq!((lines, summary), (Some(5), Some(true)), "Summary sets mode");
apply_spill_command(SpillCommand::Excerpt, &mut lines, &mut summary);
assert_eq!((lines, summary), (Some(5), Some(false)));
apply_spill_command(SpillCommand::Status, &mut lines, &mut summary);
assert_eq!(
(lines, summary),
(Some(5), Some(false)),
"Status is read-only"
);
}
#[test]
fn search_commands_parse_expected_forms() {
assert_eq!(
parse_search_command("/search").unwrap(),
SearchCommand::Help
);
assert_eq!(
parse_search_command("/search help").unwrap(),
SearchCommand::Help
);
assert_eq!(
parse_search_command("/search auth handshake").unwrap(),
SearchCommand::Query("auth handshake".into())
);
assert_eq!(
parse_search_command("/search preview").unwrap(),
SearchCommand::Preview(1)
);
assert_eq!(
parse_search_command("/search preview 3").unwrap(),
SearchCommand::Preview(3)
);
assert_eq!(
parse_search_command("/search model").unwrap(),
SearchCommand::Model
);
assert_eq!(
parse_search_command("/search rejects").unwrap(),
SearchCommand::Rejects
);
assert_eq!(
parse_search_command("/search pin 2").unwrap(),
SearchCommand::Pin(2)
);
assert_eq!(
parse_search_command("/search exclude 1").unwrap(),
SearchCommand::Exclude(1)
);
assert_eq!(
parse_search_command("/search status").unwrap(),
SearchCommand::Status
);
assert_eq!(
parse_search_command("/search clear").unwrap(),
SearchCommand::Clear
);
assert!(parse_search_command("/searching foo").is_err());
assert!(parse_search_command("/search pin").is_err());
}
#[test]
fn trace_seeds_the_detail_knob_so_the_first_toggle_collapses() {
const CONFIGURED: usize = 3;
assert_eq!(initial_spill_override(false), None);
assert_eq!(
effective_spill_lines(CONFIGURED, initial_spill_override(false)),
CONFIGURED
);
let seeded = initial_spill_override(true);
assert_eq!(seeded, Some(0), "--trace means show me everything");
assert_eq!(effective_spill_lines(CONFIGURED, seeded), 0);
let after_first_press = toggle_spill_detail(seeded, CONFIGURED);
assert_eq!(
effective_spill_lines(CONFIGURED, after_first_press),
CONFIGURED,
"the first toggle after --trace went the wrong way — this is pi's phase \
bug, and seeding exists precisely to make it unrepresentable"
);
let after_second_press = toggle_spill_detail(after_first_press, CONFIGURED);
assert_eq!(effective_spill_lines(CONFIGURED, after_second_press), 0);
}
#[test]
fn detail_toggle_shares_state_with_the_spill_command() {
let explicit = Some(7);
let expanded = toggle_spill_detail(explicit, 3);
assert_eq!(effective_spill_lines(3, expanded), 0, "toggle expands");
let collapsed = toggle_spill_detail(expanded, 3);
assert_eq!(effective_spill_lines(3, collapsed), 3);
let from_zero_config = toggle_spill_detail(Some(0), 0);
assert!(effective_spill_lines(0, from_zero_config) > 0);
}
#[test]
fn spill_override_resolves_and_reports_live_capability() {
assert_eq!(effective_spill_lines(3, None), 3);
assert_eq!(effective_spill_lines(3, Some(7)), 7);
assert_eq!(effective_spill_lines(3, Some(0)), 0);
assert_eq!(
spill_status(3, None, false, true, SpillEligibility::Available),
"spill rows: 3 (config default; live interaction available)"
);
assert_eq!(
spill_status(3, Some(7), false, true, SpillEligibility::StdoutNotTty),
"spill rows: 7 this session (config default 3; live interaction unavailable: \
stdout is not a terminal (piped or redirected))"
);
assert_eq!(
spill_status(3, Some(0), false, true, SpillEligibility::Available),
"spill rows: unbounded this session (config default 3; live viewport disabled: \
spill_lines is 0 (/spill <n> raises it))"
);
}
#[test]
fn committed_excerpt_is_full_on_lean_and_collapsed_on_rich() {
const CONFIGURED: usize = 3;
assert_eq!(committed_spill_lines(true, CONFIGURED), CONFIGURED);
assert_eq!(committed_spill_lines(true, 0), 0);
assert_eq!(committed_spill_lines(false, CONFIGURED), 0);
assert_eq!(committed_spill_lines(false, 42), 0);
assert_eq!(committed_spill_lines(false, 0), 0);
}
#[test]
fn spill_status_names_the_gate_that_refused() {
for (eligibility, needle) in [
(
SpillEligibility::UnsupportedPlatform,
"unsupported platform",
),
(SpillEligibility::FeatureDisabled, "`live-spill` feature"),
(SpillEligibility::StdinNotTty, "stdin is not a terminal"),
(SpillEligibility::StdoutNotTty, "stdout is not a terminal"),
(SpillEligibility::TermDumb, "TERM=dumb"),
] {
let status = spill_status(3, None, false, true, eligibility);
assert!(
status.contains(needle),
"{eligibility:?} must surface {needle:?} to the operator, got: {status}"
);
}
let zero = spill_status(0, None, false, true, SpillEligibility::Available);
assert!(zero.contains("spill_lines is 0"), "{zero}");
assert!(
!zero.contains("unavailable"),
"zero rows is a config choice, not a broken terminal: {zero}"
);
}
#[test]
fn spill_eligibility_reports_the_most_fundamental_refusal_first() {
assert_eq!(
spill_eligibility_for(false, false, false, false, Some("dumb"), false),
SpillEligibility::UnsupportedPlatform
);
assert_eq!(
spill_eligibility_for(true, false, false, false, Some("dumb"), false),
SpillEligibility::FeatureDisabled
);
assert_eq!(
spill_eligibility_for(true, true, false, false, Some("dumb"), false),
SpillEligibility::StdinNotTty
);
assert_eq!(
spill_eligibility_for(true, true, true, false, Some("dumb"), false),
SpillEligibility::StdoutNotTty
);
assert_eq!(
spill_eligibility_for(true, true, true, true, Some("dumb"), false),
SpillEligibility::TermDumb
);
assert_eq!(
spill_eligibility_for(true, true, true, true, Some("xterm-256color"), false),
SpillEligibility::Available
);
assert_eq!(
spill_eligibility_for(true, true, true, true, Some("xterm-256color"), true),
SpillEligibility::TerminalOwnedByCockpit
);
assert_eq!(
spill_eligibility_for(true, true, true, true, Some("dumb"), true),
SpillEligibility::TermDumb,
"a dumb terminal is the more fundamental refusal and still outranks it"
);
assert_eq!(
spill_eligibility_for(false, true, true, true, Some("xterm-256color"), true),
SpillEligibility::UnsupportedPlatform,
"and so does a platform the viewport could never run on"
);
let cockpit = SpillEligibility::TerminalOwnedByCockpit.explain();
assert!(cockpit.contains("cockpit"), "{cockpit}");
assert!(
cockpit.contains("/spill open"),
"an unavailability the operator cannot act on is barely better than a \
bare `unavailable`: {cockpit}"
);
assert_eq!(
spill_eligibility_for(true, true, true, true, None, false),
SpillEligibility::Available
);
}
#[test]
fn capable_bool_agrees_with_typed_eligibility() {
for platform in [true, false] {
for feature in [true, false] {
for stdin in [true, false] {
for stdout in [true, false] {
for term in [Some("xterm"), Some("dumb"), None] {
for cockpit in [true, false] {
assert_eq!(
live_spill_capable_for(
platform, feature, stdin, stdout, term, cockpit
),
spill_eligibility_for(
platform, feature, stdin, stdout, term, cockpit
) == SpillEligibility::Available,
"disagreement at platform={platform} feature={feature} \
stdin={stdin} stdout={stdout} term={term:?} \
cockpit={cockpit}"
);
}
}
}
}
}
}
}
#[test]
fn live_spill_requires_a_supported_interactive_terminal() {
assert!(live_spill_capable_for(
true,
true,
true,
true,
Some("xterm-256color"),
false
));
assert!(!live_spill_capable_for(
false,
true,
true,
true,
Some("xterm"),
false
));
assert!(!live_spill_capable_for(
true,
false,
true,
true,
Some("xterm"),
false
));
assert!(!live_spill_capable_for(
true,
true,
false,
true,
Some("xterm"),
false
));
assert!(!live_spill_capable_for(
true,
true,
true,
false,
Some("xterm"),
false
));
assert!(!live_spill_capable_for(
true,
true,
true,
true,
Some("dumb"),
false
));
}
#[cfg(feature = "live-spill")]
#[test]
fn mouse_tier_requires_optin_and_a_supported_interactive_terminal() {
assert!(mouse_capable_for(
true,
true,
true,
true,
Some("xterm-256color"),
true
));
assert!(!mouse_capable_for(
true,
true,
true,
true,
Some("xterm"),
false
));
assert!(!mouse_capable_for(
false,
true,
true,
true,
Some("xterm"),
true
)); assert!(!mouse_capable_for(
true,
false,
true,
true,
Some("xterm"),
true
)); assert!(!mouse_capable_for(
true,
true,
false,
true,
Some("xterm"),
true
)); assert!(!mouse_capable_for(
true,
true,
true,
false,
Some("xterm"),
true
)); assert!(!mouse_capable_for(
true,
true,
true,
true,
Some("dumb"),
true
)); }
#[cfg(feature = "live-spill")]
#[test]
fn mouse_viewport_optin_reads_from_config_default_off() {
let empty = newt_core::Config::default();
assert!(!mouse_viewport(&empty), "default is off");
let opted_in = newt_core::Config {
tui: Some(newt_core::TuiConfig {
mouse_viewport: true,
..Default::default()
}),
..Default::default()
};
assert!(mouse_viewport(&opted_in));
}
#[test]
fn slash_help_returns_true() {
assert!(dispatch_slash("/help", "/ws", false, false, false).unwrap());
}
#[test]
fn slash_parts_ignore_extra_whitespace_before_model_name() {
assert_eq!(
slash_parts("/model Ornith-1.5-35B-Q8_0"),
("model", "Ornith-1.5-35B-Q8_0", "")
);
}
#[test]
fn help_request_recognizes_every_form() {
assert_eq!(help_request("/models --help").as_deref(), Some("models"));
assert_eq!(help_request("/models -h").as_deref(), Some("models"));
assert_eq!(help_request("/probe help").as_deref(), Some("probe"));
assert_eq!(help_request("/help models").as_deref(), Some("models"));
assert_eq!(help_request("/help"), None);
assert_eq!(help_request("/models capabilities"), None);
assert_eq!(help_request("/model qwen3:30b"), None);
}
#[test]
fn help_request_treats_start_and_rename_titles_as_titles_not_help() {
assert_eq!(help_request("/start help me debug"), None);
assert_eq!(help_request("/rename fix the -h flag"), None);
assert_eq!(help_request("/start My Title"), None);
assert_eq!(help_request("/start --help").as_deref(), Some("new"));
assert_eq!(help_request("/rename -h").as_deref(), Some("conversation"));
assert_eq!(help_request("/name help me debug"), None);
assert_eq!(help_request("/name fix the -h flag"), None);
assert_eq!(help_request("/name -h").as_deref(), Some("conversation"));
}
#[test]
fn help_request_treats_a_text_settings_value_as_a_value_not_help() {
assert_eq!(help_request("/settings prompt \"help me\""), None);
assert_eq!(help_request("/settings prompt [$TIME] -h > "), None);
assert_eq!(
help_request("/settings prompt --help").as_deref(),
Some("settings")
);
assert_eq!(
help_request("/settings --help").as_deref(),
Some("settings")
);
assert_eq!(
help_request("/settings mode --help").as_deref(),
Some("settings")
);
}
#[test]
fn command_help_covers_every_listed_command_and_folds_aliases() {
for cmd in [
"settings",
"models",
"model",
"backend",
"backends",
"thinking",
"probe",
"memory",
"compress",
"summarizer",
"rounds",
"tool-rounds",
"max-rounds",
"remember",
"new",
"end",
"restart",
"conversation",
"recall",
"persona",
"crew",
"dgx",
"permissions",
"mode",
"posture",
"loadout",
"plan",
"status",
"info",
"docs",
"workspace",
"spill",
"config",
"prompt",
"allow",
"vi",
"emacs",
"nano",
"version",
"exit",
"quit",
"help",
] {
assert!(command_help_page(cmd).is_some(), "no help page for /{cmd}");
}
assert!(command_help_page("bogus").is_none());
assert_eq!(command_help_page("restart"), command_help_page("new"));
assert_eq!(command_help_page("emacs"), command_help_page("vi"));
assert_eq!(command_help_page("quit"), command_help_page("exit"));
assert_eq!(command_help_page("plan"), command_help_page("roadmap"));
assert_eq!(command_help_page("allow"), command_help_page("permissions"));
assert!(help_lines().iter().any(|l| l.contains("/status")));
assert!(help_lines().iter().any(|l| l.contains("/allow")));
let topics = help_lines()
.iter()
.find(|l| l.contains("/status <topic>"))
.expect("the /status topic line is advertised");
for topic in [
"config",
"version",
"workspace",
"loadout",
"byline",
"memory",
"models",
] {
assert!(topics.contains(topic), "{topics:?} omits the {topic} topic");
}
for retired in ["loadout", "info", "docs", "version", "workspace", "models"] {
assert!(
command_help_page(retired).is_some(),
"/help {retired} lost its page; a retired verb keeps its pointer"
);
}
assert!(help_lines().iter().any(|l| l.contains("/roadmap")));
assert!(help_lines().iter().any(|l| l.contains("/psyche")));
assert!(!help_lines()
.iter()
.any(|l| l.contains("/tenacity") || l.contains("/cognition")));
let tenacity_page = command_help_page("tenacity").expect("tenacity redirect page");
assert!(
tenacity_page.contains("/psyche tenacity"),
"{tenacity_page}"
);
let initiative_page = command_help_page("initiative").expect("initiative help page");
for level in newt_core::Initiative::all() {
assert!(
initiative_page.contains(level.label()),
"the page lists every level from Initiative::all(): {initiative_page}"
);
}
let cognition_page = command_help_page("cognition").expect("cognition redirect page");
assert!(
cognition_page.contains("/psyche cognition"),
"{cognition_page}"
);
let spill_help = command_help_page("spill").expect("spill help page");
assert!(spill_help.contains("Space or Enter"));
assert!(spill_help.contains("⧉"));
assert!(spill_help.contains("▣"));
assert!(!print_command_help("bogus", false, false, false));
}
#[test]
fn render_help_is_byte_identical_to_the_plain_help_corpus() {
for &(color, verbose) in &[(false, false), (true, false), (false, true), (true, true)] {
let mut expected = newt_line("Available commands:", color, verbose);
expected.push('\n');
for line in help_lines() {
expected.push_str(line);
expected.push('\n');
}
assert_eq!(
render_help(None, color, verbose),
expected,
"render_help(None) drifted from the help_lines corpus (color={color}, verbose={verbose})"
);
for cmd in ["dgx", "help", "exit", "quit", "emacs"] {
let page = command_help_page(cmd).expect("help page exists");
let mut want = newt_line(
&format!("/{} help", canonical_help_topic(cmd)),
color,
verbose,
);
want.push('\n');
for line in page.lines() {
want.push_str(line);
want.push('\n');
}
assert_eq!(
render_help(Some(cmd), color, verbose),
want,
"render_help(Some({cmd:?})) drifted from command_help_page"
);
}
let mut miss = newt_line(
"no help for '/bogus' — /help lists every command",
color,
verbose,
);
miss.push('\n');
assert_eq!(render_help(Some("bogus"), color, verbose), miss);
}
}
#[test]
fn rich_help_uses_the_default_markdown_policy_and_honors_off() {
let default_cfg = newt_core::Config::default();
let markdown = markdown_enabled(&default_cfg, true);
assert!(markdown, "RichTUI defaults to rendered Markdown");
let rendered = render_help_for_tui(None, true, false, markdown, 100);
let heading =
newt_core::tty::theme::active().ansi(newt_core::tty::theme::Role::MarkdownHeading);
assert!(
rendered.starts_with(&format!("▸ {heading}Available")),
"the narrator prefix must stay outside the Markdown parser: {rendered:?}"
);
assert!(!rendered.contains("## Available commands"));
assert!(rendered.contains("commands"));
assert!(rendered.contains("• "));
assert!(rendered.contains("/status"));
let cfg_off = newt_core::Config {
tui: Some(newt_core::TuiConfig {
markdown: newt_core::MarkdownMode::Off,
..Default::default()
}),
..Default::default()
};
let markdown = markdown_enabled(&cfg_off, true);
assert!(!markdown);
assert_eq!(
render_help_for_tui(None, true, false, markdown, 100),
render_help(None, true, false)
);
}
#[test]
fn slash_version_returns_true() {
assert!(dispatch_slash("/version", "/ws", false, false, false).unwrap());
}
#[test]
fn slash_workspace_returns_true() {
assert!(dispatch_slash("/workspace", "/ws", false, false, false).unwrap());
}
#[test]
fn permission_audit_lines_lists_newest_entries_and_reports_bad_lines_as_breaks() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("permission-log.jsonl");
newt_core::permission_journal::append_record(
&path,
newt_core::PermissionRecord::new(
"c",
"run_command",
newt_core::DenialKind::Exec,
"/bin/echo",
"allow",
"session",
),
)
.unwrap();
{
let mut contents = std::fs::read_to_string(&path).unwrap();
contents.push_str("this is not json\n");
std::fs::write(&path, contents).unwrap();
}
newt_core::permission_journal::append_record(
&path,
newt_core::PermissionRecord::new(
"c",
"run_command",
newt_core::DenialKind::Net,
"https://example.com",
"deny",
"once",
),
)
.unwrap();
let lines = permission_audit_lines(&path, 5);
assert!(
lines[0].starts_with("!! CHAIN BROKEN"),
"an unparseable line must be reported, not silently dropped: {lines:?}"
);
let audit_header = lines
.iter()
.position(|l| l == "permission audit: 2 of 2 (newest first)")
.expect("audit header must still be present");
assert!(lines[audit_header + 1].contains("deny"));
assert!(lines[audit_header + 1].contains("once"));
assert!(lines[audit_header + 1].contains("net"));
assert!(lines[audit_header + 1].contains("https://example.com"));
assert!(lines[audit_header + 2].contains("allow"));
let limited = permission_audit_lines(&path, 1);
assert_eq!(
limited[limited.len() - 2..],
[
"permission audit: 1 of 2 (newest first)".to_string(),
" deny once net https://example.com via run_command".to_string()
]
);
}
#[test]
fn permission_audit_lines_surfaces_the_pre_chain_sibling() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("permission-log.jsonl");
let pre_chain = newt_core::permission_journal::pre_chain_path(&path);
let legacy = newt_core::PermissionRecord::new(
"c",
"run_command",
newt_core::DenialKind::Exec,
"/bin/legacy",
"allow",
"once",
);
std::fs::write(
&pre_chain,
format!("{}\n", serde_json::to_string(&legacy).unwrap()),
)
.unwrap();
newt_core::permission_journal::append_record(
&path,
newt_core::PermissionRecord::new(
"c",
"run_command",
newt_core::DenialKind::Exec,
"/bin/new",
"allow",
"once",
),
)
.unwrap();
let lines = permission_audit_lines(&path, 5);
assert!(
lines
.iter()
.any(|l| l.contains("pre-chain migration")
&& l.contains(&pre_chain.display().to_string())),
"must name the pre-chain sibling: {lines:?}"
);
assert!(
lines
.iter()
.any(|l| l.contains("/bin/legacy") && l.contains("unchained")),
"must render the unchained legacy record: {lines:?}"
);
}
#[test]
fn permission_audit_lines_reads_an_unrotated_flat_log() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("permission-log.jsonl");
let legacy = newt_core::PermissionRecord::new(
"c",
"run_command",
newt_core::DenialKind::Exec,
"/bin/legacy",
"allow",
"once",
);
std::fs::write(
&path,
format!("{}\n", serde_json::to_string(&legacy).unwrap()),
)
.unwrap();
let lines = permission_audit_lines(&path, 5);
assert!(
!lines.iter().any(|l| l == "no permission log entries yet"),
"a flat pre-chain body must not read as empty: {lines:?}"
);
assert!(
lines
.iter()
.any(|l| l.contains("/bin/legacy") && l.contains("unchained")),
"must render the unrotated legacy record: {lines:?}"
);
}
#[test]
fn loadout_is_reachable_as_a_status_topic_and_keeps_its_help_page() {
assert!(help_lines()
.iter()
.any(|l| l.contains("/status <topic>") && l.contains("loadout")));
assert!(command_help_page("loadout").is_some());
}
#[test]
fn loadout_view_renders_declared_and_resolved() {
let l = newt_core::Loadout {
provider: Some("dgx".into()),
model: Some("nemotron@deep".into()),
kit: Some("nemotron".into()),
profile: Some("nemotron".into()),
role: Some("python-developer".into()),
settings: Some(newt_core::LoadoutSettings {
num_ctx: Some(24576),
framing: Some("Ship small.".into()),
}),
};
let pick = newt_core::config::ProfilePick {
name: "nemotron".into(),
via: newt_core::config::PickVia::Bundle("nemotron".into()),
};
let view = LoadoutView {
name: Some("dev"),
loadout: Some(&l),
inf_url: "http://dgx:11434",
inf_model: "nemotron-3:33b",
profile_pick: Some(&pick),
persona: Some("python-developer"),
};
let out = view.render();
assert!(out.contains("Active loadout: dev"), "{out}");
assert!(
out.contains("declared:") && out.contains("nemotron@deep"),
"{out}"
);
assert!(
out.contains("24576") && out.contains("Ship small."),
"{out}"
);
assert!(out.contains("nemotron-3:33b @ http://dgx:11434"), "{out}");
assert!(out.contains("via bundle 'nemotron'"), "{out}");
assert!(out.contains("python-developer"), "{out}");
}
#[test]
fn loadout_view_renders_when_none_active() {
let view = LoadoutView {
name: None,
loadout: None,
inf_url: "http://localhost:11434",
inf_model: "llama3.1:8b",
profile_pick: None,
persona: None,
};
let out = view.render();
assert!(out.contains("No loadout active."), "{out}");
assert!(
out.contains("llama3.1:8b @ http://localhost:11434"),
"{out}"
);
assert!(out.contains("profile") && out.contains("(none)"), "{out}");
}
#[test]
fn slash_config_returns_true() {
assert!(dispatch_slash("/config", "/ws", false, false, false).unwrap());
}
#[test]
fn config_is_reachable_as_a_status_topic_and_keeps_its_help_page() {
assert!(help_lines()
.iter()
.any(|l| l.contains("/status <topic>") && l.contains("config")));
assert!(command_help_page("config").is_some());
assert!(dispatch_slash("/config", "/ws", false, false, false).unwrap());
}
#[test]
fn slash_unknown_returns_true() {
assert!(dispatch_slash("/notacommand", "/ws", false, false, false).unwrap());
}
#[test]
fn retired_dials_through_the_real_router_mutate_nothing() {
use newt_core::cognition::{cli_cognition, set_cli_cognition, CognitionOverride};
use newt_core::tenacity::{cli_tenacity, set_cli_tenacity, Tenacity};
let _g = newt_core::test_guard::GlobalSettingsGuard::acquire();
set_cli_cognition(CognitionOverride::Unset);
set_cli_tenacity(Tenacity::Normal);
assert!(dispatch_slash("/tenacity relentless", "/ws", false, false, false).unwrap());
assert!(dispatch_slash("/cognition meticulous", "/ws", false, false, false).unwrap());
assert_eq!(cli_tenacity(), Some(Tenacity::Normal));
assert_eq!(cli_cognition(), CognitionOverride::Unset);
assert!(dispatch_slash("/psyche tenacity relentless", "/ws", false, false, false).unwrap());
assert_eq!(cli_tenacity(), Some(Tenacity::Relentless));
}
#[test]
fn slash_dgx_no_subcmd_returns_true() {
assert!(dispatch_slash("/dgx", "/ws", false, false, false).unwrap());
}
#[test]
fn restore_seam_refuses_a_tampered_transcript_and_modifies_nothing() {
let state = tempfile::tempdir().unwrap();
let workspace = tempfile::tempdir().unwrap();
let store = newt_core::ConversationStore::new(state.path(), workspace.path(), 100).unwrap();
let personas = PersonaStore::new(state.path().join("personas"));
let id = store.create("tamper target", None).unwrap();
store.append_turn(&id, "u1", "a1").unwrap();
store.append_turn(&id, "u2", "a2").unwrap();
store.append_turn(&id, "u3", "a3").unwrap();
assert!(
prepare_conversation_restore(&store, &personas, &id).is_ok(),
"the untampered conversation must prepare"
);
let conn = rusqlite::Connection::open(state.path().join("conversations.db")).unwrap();
let changed = conn
.execute(
"UPDATE turns SET assistant = 'forged' WHERE conversation_id = ?1
AND seq = (SELECT MIN(seq) + 1 FROM turns WHERE conversation_id = ?1)",
rusqlite::params![&id],
)
.unwrap();
assert_eq!(changed, 1, "the tamper must have actually landed");
let err = match prepare_conversation_restore(&store, &personas, &id) {
Ok(_) => panic!("the tampered conversation must refuse to prepare"),
Err(e) => e,
};
let msg = format!("{err}");
assert!(
msg.contains("chain violation") && msg.contains(&id),
"the refusal must be an integrity diagnosis naming the conversation: {msg}"
);
let rec = store.load(&id).unwrap();
assert_eq!(rec.turns.len(), 3, "refusal must not add or drop rows");
assert_eq!(
rec.turns[1].assistant, "forged",
"refusal must not repair or re-chain the tampered row"
);
conn.execute(
"UPDATE turns SET assistant = 'a2' WHERE conversation_id = ?1
AND seq = (SELECT MIN(seq) + 1 FROM turns WHERE conversation_id = ?1)",
rusqlite::params![&id],
)
.unwrap();
conn.execute(
"UPDATE turns SET assistant = 'forged tail' WHERE conversation_id = ?1
AND seq = (SELECT MAX(seq) FROM turns WHERE conversation_id = ?1)",
rusqlite::params![&id],
)
.unwrap();
let err = match prepare_conversation_restore(&store, &personas, &id) {
Ok(_) => panic!("a tampered final turn must also refuse to prepare"),
Err(e) => e,
};
let msg = format!("{err}");
assert!(
msg.contains("chain violation") && msg.contains("tip witness"),
"final-turn tamper must be caught BY THE WITNESS: {msg}"
);
}