use serde::Serialize;
use std::path::{Path, PathBuf};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use trusty_agents_common::compress::{
CompressionPath, RtkResolver, compress_tool_output_async_with_path_using, default_rtk_resolver,
};
const EXIT_SENTINEL_PREFIX: &str = "__tm_compress_exit=";
const EXIT_SENTINEL_SUFFIX: &str = "__";
pub(crate) fn wrap_command_reporting_exit(command: &str) -> String {
format!(
"{{ {command}; printf '\\n{EXIT_SENTINEL_PREFIX}%s{EXIT_SENTINEL_SUFFIX}\\n' \"$?\"; }}"
)
}
fn split_exit_sentinel(stdin_text: &str) -> (&str, Option<i32>) {
let body = stdin_text.strip_suffix('\n').unwrap_or(stdin_text);
let (head, last) = match body.rfind('\n') {
Some(newline) => (&body[..newline], &body[newline + 1..]),
None => ("", body),
};
match parse_exit_sentinel(last) {
Some(code) => (head, Some(code)),
None => (stdin_text, None),
}
}
fn parse_exit_sentinel(line: &str) -> Option<i32> {
let digits = line
.strip_prefix(EXIT_SENTINEL_PREFIX)?
.strip_suffix(EXIT_SENTINEL_SUFFIX)?;
let code: i32 = digits.parse().ok()?;
(0..=255).contains(&code).then_some(code)
}
pub(crate) async fn run_compress(tool: &str) -> anyhow::Result<()> {
init_stats_log_subscriber();
let mut stdin_text = String::new();
tokio::io::stdin().read_to_string(&mut stdin_text).await?;
let (input, exit) = split_exit_sentinel(&stdin_text);
let started = std::time::Instant::now();
let (compressed, path) = compress_with_raw_fallback(default_rtk_resolver(), tool, input).await;
let duration_ms = u64::try_from(started.elapsed().as_millis()).unwrap_or(u64::MAX);
let _pct = log_compression_stats(tool, input.len(), compressed.len(), path.as_str(), exit);
append_compression_record(
&compression_log_path(),
tool,
input.len(),
compressed.len(),
path.as_str(),
duration_ms,
)
.await;
record_compress_savings(input.len(), compressed.len(), path.as_str());
let mut stdout = tokio::io::stdout();
stdout.write_all(compressed.as_bytes()).await?;
stdout.flush().await?;
Ok(())
}
async fn compress_with_raw_fallback(
resolve: RtkResolver,
tool: &str,
input: &str,
) -> (String, CompressionPath) {
let (compressed, path) = compress_tool_output_async_with_path_using(resolve, tool, input).await;
if compressed.trim().is_empty() && !input.trim().is_empty() {
tracing::warn!(
tool_name = %tool,
bytes_before = input.len(),
compression_path = %path.as_str(),
"compression returned no output for a non-empty input; falling back to the raw output"
);
return (input.to_string(), path);
}
(compressed, path)
}
fn record_compress_savings(bytes_before: usize, bytes_after: usize, compression_path: &str) {
let root = match crate::commands::managed_root::resolve_managed_paths(None) {
Ok(paths) => Some(paths.root),
Err(source) => {
tracing::warn!(
%source,
"cannot resolve the framework root: writing no compress savings row"
);
None
}
};
record_compress_savings_with(
root.as_deref(),
trusty_mpm::core::savings::claude_code_session_id().as_deref(),
bytes_before,
bytes_after,
compression_path,
);
}
fn record_compress_savings_with(
root: Option<&std::path::Path>,
session_id: Option<&str>,
bytes_before: usize,
bytes_after: usize,
compression_path: &str,
) {
let Some(session_id) = session_id.map(str::trim).filter(|id| !id.is_empty()) else {
tracing::debug!("no claude session id: writing no compress savings row");
return;
};
let Some(root) = root else {
return;
};
trusty_mpm::core::savings_compress::record_compress(
root,
session_id,
bytes_before,
bytes_after,
compression_path,
);
}
fn init_stats_log_subscriber() {
let _ = tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
)
.with_writer(std::io::stderr)
.try_init();
}
fn log_compression_stats(
tool_name: &str,
bytes_before: usize,
bytes_after: usize,
path: &str,
exit: Option<i32>,
) -> f64 {
let pct_reduction = if bytes_before > 0 {
(1.0 - bytes_after as f64 / bytes_before as f64) * 100.0
} else {
0.0
};
if !stats_line_is_informative(bytes_before, bytes_after, exit) {
return pct_reduction;
}
let exit_status = exit.map_or_else(|| "unknown".to_string(), |code| code.to_string());
tracing::info!(
tool_name = %tool_name,
bytes_before,
bytes_after,
pct_reduction,
compression_path = %path,
exit = %exit_status,
"tool output compressed"
);
pct_reduction
}
fn stats_line_is_informative(bytes_before: usize, bytes_after: usize, exit: Option<i32>) -> bool {
bytes_after != bytes_before || exit.is_some_and(|code| code != 0)
}
#[derive(Debug, Clone, Serialize)]
struct CompressionRecord {
ts: String,
session_id: Option<String>,
surface: &'static str,
surface_detail: String,
tokens_before: u32,
tokens_after: u32,
ratio: f64,
working_context_pct_after: Option<u8>,
overhead_pct_after: Option<u8>,
compaction_event: bool,
duration_ms: u64,
rounds: u32,
compression_path: String,
}
fn estimate_tokens_from_bytes(bytes: usize) -> u32 {
u32::try_from(bytes / 4).unwrap_or(u32::MAX)
}
fn compression_log_path() -> PathBuf {
match dirs::home_dir() {
Some(home) => home.join(".trusty-mpm").join("compression.jsonl"),
None => PathBuf::from(".trusty-mpm").join("compression.jsonl"),
}
}
async fn append_compression_record(
path: &Path,
tool_name: &str,
bytes_before: usize,
bytes_after: usize,
compression_path: &str,
duration_ms: u64,
) {
let tokens_before = estimate_tokens_from_bytes(bytes_before);
let tokens_after = estimate_tokens_from_bytes(bytes_after);
let ratio = if tokens_before > 0 {
f64::from(tokens_after) / f64::from(tokens_before)
} else {
0.0
};
let record = CompressionRecord {
ts: chrono::Utc::now().to_rfc3339(),
session_id: None,
surface: "tm-compress",
surface_detail: tool_name.to_string(),
tokens_before,
tokens_after,
ratio,
working_context_pct_after: None,
overhead_pct_after: None,
compaction_event: false,
duration_ms,
rounds: 1,
compression_path: compression_path.to_string(),
};
if let Some(parent) = path.parent() {
let _ = tokio::fs::create_dir_all(parent).await;
}
let line = match serde_json::to_string(&record) {
Ok(s) => format!("{s}\n"),
Err(e) => {
tracing::debug!(error = %e, "compression telemetry: serialize failed");
return;
}
};
match tokio::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)
.await
{
Ok(mut f) => {
if let Err(e) = f.write_all(line.as_bytes()).await {
tracing::debug!(error = %e, path = %path.display(), "compression telemetry: write failed");
return;
}
if let Err(e) = f.flush().await {
tracing::debug!(error = %e, path = %path.display(), "compression telemetry: flush failed");
}
}
Err(e) => {
tracing::debug!(error = %e, path = %path.display(), "compression telemetry: open failed");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use trusty_agents_common::compress::no_rtk;
async fn native_compress(tool: &str, input: &str) -> (String, CompressionPath) {
compress_tool_output_async_with_path_using(no_rtk, tool, input).await
}
#[test]
fn log_compression_stats_pct_reduction_is_zero_for_empty_input() {
assert_eq!(
log_compression_stats("bash", 0, 0, "native_fallback", None),
0.0
);
}
#[tokio::test]
async fn native_fallback_elision_reports_a_matching_non_zero_reduction() {
let mut input = String::new();
for i in 0..90 {
input.push_str(&format!(
"crates/x/src/lib.rs:{i}: fn candidate_{i}() -> bool\n"
));
}
let (compressed, path) = native_compress("grep -n", &input).await;
assert_eq!(path.as_str(), "native_fallback");
assert!(
compressed.contains("lines omitted"),
"expected the line-list cap to elide: {compressed}"
);
assert!(compressed.len() < input.len(), "expected fewer bytes out");
let pct = log_compression_stats(
"grep -n",
input.len(),
compressed.len(),
path.as_str(),
Some(0),
);
let expected = (1.0 - compressed.len() as f64 / input.len() as f64) * 100.0;
assert!(
(pct - expected).abs() < f64::EPSILON,
"reported {pct} does not match the byte delta {expected}"
);
assert!(pct > 0.0, "an eliding compression must not report 0.0");
}
#[test]
fn log_compression_stats_pct_reduction_can_be_negative_when_output_expands() {
assert_eq!(
log_compression_stats("bash", 10, 20, "native_fallback", None),
-100.0
);
}
#[test]
fn a_passthrough_run_emits_no_stats_line() {
assert!(
!stats_line_is_informative(668, 668, Some(0)),
"an unchanged payload from a command that succeeded says nothing"
);
assert!(
!stats_line_is_informative(668, 668, None),
"an unwrapped invocation reports no failure, so it is no different"
);
assert!(
!stats_line_is_informative(0, 0, Some(0)),
"an empty payload is the same no-op"
);
}
#[test]
fn a_failing_wrapped_command_still_emits_the_stats_line() {
assert!(stats_line_is_informative(0, 0, Some(1)));
assert!(stats_line_is_informative(668, 668, Some(3)));
assert!(stats_line_is_informative(0, 0, Some(137)));
}
#[test]
fn an_expanding_run_still_emits_the_stats_line() {
assert!(stats_line_is_informative(10, 20, None));
assert!(stats_line_is_informative(1076, 36, Some(0)));
}
#[tokio::test]
async fn run_compress_shrinks_repetitive_cargo_test_output() {
let mut input = String::new();
for i in 0..50 {
input.push_str(&format!("test mod::t{i} ... ok\n"));
}
input.push_str("test result: ok. 50 passed; 0 failed\n");
let (compressed, _path) = native_compress("cargo test", &input).await;
assert!(
compressed.len() < input.len(),
"expected compression to shrink repetitive passing-test output"
);
assert!(compressed.contains("test result"));
}
#[test]
fn record_compress_savings_with_declines_without_a_session_id() {
let root = tempfile::tempdir().expect("temp root");
for id in [None, Some(""), Some(" ")] {
record_compress_savings_with(Some(root.path()), id, 10_000, 1_000, "native");
}
record_compress_savings_with(None, Some("c1"), 10_000, 1_000, "native");
assert!(
!root.path().join("usage").exists(),
"no session id, or no root, must write nothing at all"
);
}
#[test]
fn record_compress_savings_with_writes_under_the_named_root() {
let root = tempfile::tempdir().expect("temp root");
record_compress_savings_with(Some(root.path()), Some("c-7514"), 10_000, 1_000, "native");
let ledger = trusty_mpm::core::savings::savings_log_in(root.path());
let written = std::fs::read_to_string(&ledger).expect("the named root must hold the row");
assert!(
written.contains("c-7514") && written.contains("\"technique\":\"compress\""),
"the row must be keyed by the supplied session id: {written}"
);
}
#[tokio::test]
async fn run_compress_passes_through_short_output_unchanged() {
let input = "ok\n";
let (compressed, _path) = native_compress("bash", input).await;
assert_eq!(compressed, input);
}
#[test]
fn compression_record_serializes_to_valid_jsonl() {
let record = CompressionRecord {
ts: "2026-07-24T19:00:00Z".to_string(),
session_id: None,
surface: "tm-compress",
surface_detail: "cargo test".to_string(),
tokens_before: 100,
tokens_after: 25,
ratio: 0.25,
working_context_pct_after: None,
overhead_pct_after: None,
compaction_event: false,
duration_ms: 4,
rounds: 1,
compression_path: "native_fallback".to_string(),
};
let json = serde_json::to_string(&record).unwrap();
assert!(!json.contains('\n'), "JSONL invariant: single line");
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["surface"], "tm-compress");
assert_eq!(parsed["surface_detail"], "cargo test");
assert_eq!(parsed["ratio"], 0.25);
assert_eq!(parsed["duration_ms"], 4);
assert_eq!(parsed["rounds"], 1);
assert_eq!(parsed["compaction_event"], false);
assert_eq!(parsed["compression_path"], "native_fallback");
assert!(parsed["session_id"].is_null());
assert!(parsed["working_context_pct_after"].is_null());
assert!(parsed["overhead_pct_after"].is_null());
}
#[tokio::test]
async fn append_compression_record_creates_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("compression.jsonl");
append_compression_record(&path, "cargo test", 400, 100, "native_fallback", 7).await;
assert!(path.exists(), "compression.jsonl should be created");
let contents = tokio::fs::read_to_string(&path).await.unwrap();
let lines: Vec<&str> = contents.lines().collect();
assert_eq!(lines.len(), 1, "exactly one line after one append");
let parsed: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
assert_eq!(parsed["surface"], "tm-compress");
assert_eq!(parsed["surface_detail"], "cargo test");
assert_eq!(parsed["tokens_before"], 100);
assert_eq!(parsed["tokens_after"], 25);
assert_eq!(parsed["ratio"], 0.25);
assert_eq!(parsed["duration_ms"], 7);
assert_eq!(parsed["rounds"], 1);
assert_eq!(parsed["compaction_event"], false);
assert_eq!(parsed["compression_path"], "native_fallback");
assert!(parsed["session_id"].is_null());
}
#[tokio::test]
async fn append_compression_record_appends() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("nested").join("compression.jsonl");
append_compression_record(&path, "cargo test", 400, 100, "rtk_binary", 1).await;
append_compression_record(&path, "git diff", 200, 200, "native_fallback", 2).await;
let contents = tokio::fs::read_to_string(&path).await.unwrap();
let lines: Vec<&str> = contents.lines().collect();
assert_eq!(lines.len(), 2, "second append should not overwrite");
let p1: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
let p2: serde_json::Value = serde_json::from_str(lines[1]).unwrap();
assert_eq!(p1["surface_detail"], "cargo test");
assert_eq!(p1["compression_path"], "rtk_binary");
assert_eq!(p2["surface_detail"], "git diff");
assert_eq!(
p2["ratio"], 1.0,
"unchanged-size compression must report ratio 1.0, not 0.0"
);
}
#[tokio::test]
async fn unwritable_log_dir_does_not_panic_or_block_the_append() {
let dir = tempfile::tempdir().unwrap();
let blocked = dir.path().join("not-a-dir");
tokio::fs::write(&blocked, b"i am a file, not a directory")
.await
.unwrap();
let path = blocked.join("compression.jsonl");
append_compression_record(&path, "cargo test", 400, 100, "native_fallback", 1).await;
assert!(
!path.exists(),
"the record must not have been written under an unwritable parent"
);
}
#[test]
fn estimate_tokens_from_bytes_divides_by_four() {
assert_eq!(estimate_tokens_from_bytes(400), 100);
assert_eq!(estimate_tokens_from_bytes(0), 0);
}
#[test]
fn compression_log_path_ends_with_trusty_mpm_compression_jsonl() {
let path = compression_log_path();
assert!(path.ends_with(".trusty-mpm/compression.jsonl"));
}
#[test]
fn split_exit_sentinel_reads_and_strips_a_trailing_status() {
let (payload, exit) = split_exit_sentinel("boom\n\n__tm_compress_exit=3__\n");
assert_eq!(payload, "boom\n", "the sentinel must not reach the caller");
assert_eq!(exit, Some(3));
let (payload, exit) = split_exit_sentinel("boom\n__tm_compress_exit=0__\n");
assert_eq!(payload, "boom");
assert_eq!(exit, Some(0));
let (payload, exit) = split_exit_sentinel("\n__tm_compress_exit=127__\n");
assert_eq!(payload, "");
assert_eq!(exit, Some(127));
let (_, exit) = split_exit_sentinel("out\n\n__tm_compress_exit=137__\n");
assert_eq!(exit, Some(137));
}
#[test]
fn split_exit_sentinel_leaves_unwrapped_input_untouched() {
for raw in ["plain output\n", "no trailing newline", "", "\n"] {
let (payload, exit) = split_exit_sentinel(raw);
assert_eq!(payload, raw, "unwrapped input must pass through: {raw:?}");
assert_eq!(exit, None);
}
}
#[test]
fn parse_exit_sentinel_rejects_out_of_range_and_malformed_lines() {
assert_eq!(parse_exit_sentinel("__tm_compress_exit=3__"), Some(3));
assert_eq!(parse_exit_sentinel("__tm_compress_exit=255__"), Some(255));
assert_eq!(parse_exit_sentinel("__tm_compress_exit=256__"), None);
assert_eq!(parse_exit_sentinel("__tm_compress_exit=-1__"), None);
assert_eq!(parse_exit_sentinel("__tm_compress_exit=ok__"), None);
assert_eq!(parse_exit_sentinel("__tm_compress_exit=3"), None);
assert_eq!(parse_exit_sentinel("test result: ok. 3 passed"), None);
}
#[test]
fn wrap_command_reporting_exit_round_trips_through_split() {
let wrapped = wrap_command_reporting_exit("cargo test");
assert!(
wrapped.starts_with("{ cargo test; printf "),
"the command must run first inside the brace group: {wrapped}"
);
assert!(
wrapped.contains(EXIT_SENTINEL_PREFIX) && wrapped.contains("\"$?\""),
"the sentinel must carry the command's own status: {wrapped}"
);
let stream = format!("out\n\n{EXIT_SENTINEL_PREFIX}3{EXIT_SENTINEL_SUFFIX}\n");
assert_eq!(split_exit_sentinel(&stream), ("out\n", Some(3)));
}
#[derive(Clone, Default)]
struct CaptureWriter(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
impl std::io::Write for CaptureWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().expect("capture lock").extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for CaptureWriter {
type Writer = CaptureWriter;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
#[cfg(unix)]
fn empty_rtk_stub_path() -> PathBuf {
std::env::temp_dir().join(format!("tm-compress-empty-rtk-{}.sh", std::process::id()))
}
#[cfg(unix)]
fn empty_rtk(_name: &str) -> Option<PathBuf> {
use std::os::unix::fs::PermissionsExt;
let path = empty_rtk_stub_path();
std::fs::write(&path, "#!/bin/sh\ncat >/dev/null\nexit 0\n").ok()?;
let mut perms = std::fs::metadata(&path).ok()?.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&path, perms).ok()?;
Some(path)
}
fn with_captured_warnings<T>(body: impl FnOnce() -> T) -> (T, String) {
crate::test_support::enable_event_capture();
let capture = CaptureWriter::default();
let subscriber = tracing_subscriber::fmt()
.with_writer(capture.clone())
.with_max_level(tracing::Level::WARN)
.with_ansi(false)
.finish();
let out = tracing::subscriber::with_default(subscriber, body);
let logged = String::from_utf8(capture.0.lock().expect("capture lock").clone())
.expect("the captured log must be utf-8");
(out, logged)
}
fn diff_stat_payload() -> String {
let mut input = String::new();
for i in 0..40 {
input.push_str(&format!(" crates/x/src/file_{i}.rs | 4 ++--\n"));
}
input.push_str(" 40 files changed, 80 insertions(+), 80 deletions(-)\n");
input
}
#[cfg(unix)]
#[test]
fn rtk_returning_nothing_falls_back_to_the_raw_output_and_warns_once() {
let input = diff_stat_payload();
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("current-thread runtime");
let ((compressed, path), logged) = with_captured_warnings(|| {
runtime.block_on(compress_with_raw_fallback(empty_rtk, "git diff", &input))
});
let _ = std::fs::remove_file(empty_rtk_stub_path());
assert_eq!(
path.as_str(),
"rtk_binary",
"the stub must have been taken as the rtk arm, else this test proves \
nothing about #7377's path: {logged}"
);
assert_eq!(
compressed, input,
"the caller must receive the original text byte-for-byte"
);
assert_eq!(
logged.matches("falling back to the raw output").count(),
1,
"the fallback must be announced exactly once: {logged}"
);
assert!(
logged.contains("rtk_binary"),
"the warning must name the path that emptied the output: {logged}"
);
}
#[tokio::test]
async fn a_real_compression_is_not_treated_as_an_empty_result() {
let mut input = String::new();
for i in 0..50 {
input.push_str(&format!("test mod::t{i} ... ok\n"));
}
input.push_str("test result: ok. 50 passed; 0 failed\n");
let (compressed, path) = compress_with_raw_fallback(no_rtk, "cargo test", &input).await;
assert_eq!(path.as_str(), "native_fallback");
assert!(
compressed.len() < input.len(),
"the guard must not have replaced a real compression"
);
}
}