use std::fmt::Write as _;
use crate::lifecycle::SubjectKind;
use crate::readouts::buf::ReadoutBuf;
use crate::readouts::context::ReadoutContext;
use crate::readouts::format::ballot_bar;
use crate::readouts::readout::{ContentMode, Lod, Readout, ReadoutOptions};
static LAST_RENDERED_COORDS: std::sync::Mutex<String> = std::sync::Mutex::new(String::new());
struct Stratum {
pairs: Vec<(String, String)>,
}
fn parse_strata(labels: &str) -> Vec<Stratum> {
if labels.is_empty() {
return Vec::new();
}
let mut strata = Vec::new();
for raw in labels.split("), (") {
let inner = raw.trim_start_matches('(').trim_end_matches(')');
let mut pairs = Vec::new();
for kv in inner.split(", ") {
match kv.split_once('=') {
Some((k, v)) => pairs.push((k.to_string(), v.to_string())),
None => pairs.push(("_".into(), kv.to_string())),
}
}
strata.push(Stratum { pairs });
}
strata
}
fn render_strata(
strata: &[Stratum],
prev_strata: &[Stratum],
color: bool,
head_consumed: usize,
available_width: usize,
continuation_indent: &str,
) -> String {
if strata.is_empty() {
return String::new();
}
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let yellow = if color { "\x1b[33m" } else { "" };
let magenta = if color { "\x1b[35m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let mut rendered: Vec<(String, usize)> = Vec::with_capacity(strata.len());
for (idx, s) in strata.iter().enumerate() {
let prior_pairs: Option<&Vec<(String, String)>> = prev_strata.get(idx).map(|p| &p.pairs);
let mut styled = String::with_capacity(64);
let mut plain = String::with_capacity(64);
styled.push_str(bold);
styled.push_str(yellow);
styled.push('(');
plain.push('(');
for (pi, (k, v)) in s.pairs.iter().enumerate() {
if pi > 0 {
styled.push_str(", ");
plain.push_str(", ");
}
let changed = match prior_pairs {
Some(prior) => prior
.iter()
.find(|(pk, _)| pk == k)
.map(|(_, pv)| pv != v)
.unwrap_or(true),
None => true,
};
write!(styled, "{k}=").ok();
plain.push_str(k);
plain.push('=');
if changed {
write!(styled, "{reset}{bold}{magenta}{v}{reset}{bold}{yellow}").ok();
} else {
styled.push_str(v);
}
plain.push_str(v);
}
styled.push(')');
styled.push_str(reset);
plain.push(')');
rendered.push((styled, plain.chars().count()));
}
let mut out = String::new();
let sep_plain_len = 2; let cont_indent_width = continuation_indent.chars().count();
let mut current_visible = head_consumed;
let mut wrote_any = false;
for (styled, plain_len) in rendered.iter() {
let needs_sep = wrote_any;
let next_width = if needs_sep {
current_visible + sep_plain_len + plain_len
} else {
current_visible + plain_len
};
let first_stratum_overflows = !wrote_any
&& current_visible + plain_len > available_width
&& cont_indent_width + plain_len < available_width;
if first_stratum_overflows {
out.push('\n');
out.push_str(continuation_indent);
out.push_str(styled);
current_visible = cont_indent_width + plain_len;
} else if wrote_any && next_width > available_width {
out.push(',');
out.push('\n');
out.push_str(continuation_indent);
out.push_str(styled);
current_visible = cont_indent_width + plain_len;
} else {
if needs_sep {
out.push_str(dim);
out.push_str(", ");
out.push_str(reset);
current_visible += sep_plain_len;
}
out.push_str(styled);
current_visible += plain_len;
}
wrote_any = true;
}
out
}
fn current_terminal_cols() -> usize {
crate::activity::terminal_cols().unwrap_or(120).max(40)
}
fn strata_diff(strata: &[Stratum], prev_strata: &[Stratum]) -> Vec<Stratum> {
if prev_strata.is_empty() {
return strata.to_vec();
}
let mut out: Vec<Stratum> = Vec::with_capacity(strata.len());
for (idx, s) in strata.iter().enumerate() {
let prior = prev_strata.get(idx);
let mut changed_pairs: Vec<(String, String)> = Vec::new();
for (k, v) in &s.pairs {
let unchanged = match prior {
Some(p) => p
.pairs
.iter()
.find(|(pk, _)| pk == k)
.map(|(_, pv)| pv == v)
.unwrap_or(false),
None => false,
};
if !unchanged {
changed_pairs.push((k.clone(), v.clone()));
}
}
if !changed_pairs.is_empty() {
out.push(Stratum {
pairs: changed_pairs,
});
}
}
out
}
impl Clone for Stratum {
fn clone(&self) -> Self {
Self {
pairs: self.pairs.clone(),
}
}
}
pub(crate) fn format_coords_block(
labels: &str,
color: bool,
head_consumed: usize,
continuation_indent: &str,
summarize_changed_only: bool,
) -> String {
if labels.is_empty() {
return String::new();
}
let strata = parse_strata(labels);
let prev = LAST_RENDERED_COORDS
.lock()
.ok()
.map(|g| g.clone())
.unwrap_or_default();
let prev_strata = parse_strata(&prev);
let display_strata: Vec<Stratum> = if summarize_changed_only {
strata_diff(&strata, &prev_strata)
} else {
strata.clone()
};
if summarize_changed_only && let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = labels.to_string();
}
if display_strata.is_empty() {
return String::new();
}
let width = current_terminal_cols();
let available = width.saturating_sub(1);
let body = render_strata(
&display_strata,
&prev_strata,
color,
head_consumed.saturating_add(1),
available,
continuation_indent,
);
let mut out = String::with_capacity(body.len() + 1);
out.push(' ');
out.push_str(&body);
out
}
pub struct PhaseOutcomeReadout;
impl Readout for PhaseOutcomeReadout {
fn name(&self) -> &'static str {
"phase_outcome"
}
fn accepts(&self) -> &'static [SubjectKind] {
&[SubjectKind::Phase]
}
fn render(
&self,
ctx: &dyn ReadoutContext,
lod: Lod,
mode: ContentMode,
_opts: &ReadoutOptions,
out: &mut dyn ReadoutBuf,
) -> usize {
match (lod, mode) {
(Lod::Compact, ContentMode::Value) => render_compact_value(ctx, out),
(Lod::Compact, ContentMode::Explanation) => render_compact_explanation(ctx, out),
(Lod::Labeled, ContentMode::Value) => render_labeled_value(ctx, out),
(Lod::Labeled, ContentMode::Explanation) => render_labeled_explanation(ctx, out),
(Lod::Expanded, ContentMode::Value) => render_expanded_value(ctx, out),
(Lod::Expanded, ContentMode::Explanation) => render_expanded_explanation(ctx, out),
}
}
}
fn status_color(outcome: &crate::phase_outcome::Outcome, color: bool) -> &'static str {
if !color {
return "";
}
use crate::phase_outcome::{Disposition, Validity};
match (outcome.disposition, outcome.validity) {
(_, Validity::Failed) => "\x1b[31m", (Disposition::Skipped, _) => "\x1b[2m", (Disposition::Interrupted, Validity::Succeeded) => "\x1b[33m", _ => "\x1b[32m", }
}
fn completion_meter(ctx: &dyn ReadoutContext) -> String {
if ctx.open_ended() {
return String::new();
}
format!(
" {:.0}%",
ctx.progress_fraction().map(|f| f * 100.0).unwrap_or(100.0)
)
}
fn render_compact_value(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let outcome = ctx.outcome();
let glyph_color = status_color(&outcome, color);
let glyph = outcome.glyph();
let meter = completion_meter(ctx);
let elapsed = ctx.elapsed_secs();
let depth_indent = ctx.depth_indent();
let name = ctx.subject_name();
let mut tmp = String::with_capacity(64);
let _ = write!(
&mut tmp,
"{depth_indent}{glyph_color}{glyph}{reset} {bold}{blue}[{name}]{reset}\
{meter} {dim}({elapsed:.2}s){reset}",
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_compact_explanation(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let green = if color { "\x1b[32m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let depth_indent = ctx.depth_indent();
let mut tmp = String::with_capacity(96);
let _ = write!(
&mut tmp,
"{depth_indent}{green}done{reset} {bold}{blue}[phase-name]{reset} \
progress% {dim}(elapsed){reset}",
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_expanded_value(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let yellow = if color { "\x1b[33m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let red = if color { "\x1b[31m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let outcome = ctx.outcome();
let glyph_color = status_color(&outcome, color);
let glyph = outcome.glyph();
let outcome_errors = ctx.outcome_errors();
let cycles = ctx.cycles_completed();
let errors = ctx.errors();
let retries = ctx.retries();
let ok = ctx.ops_ok();
let skips = ctx.skips();
let concurrency = ctx.concurrency();
let elapsed = ctx.elapsed_secs();
let consumed = ctx.consumed();
let total_extent = ctx.cycles_total();
let result_total = cycles.saturating_sub(skips).max(ok);
let ok_pct: f64 = if result_total > 0 {
ok as f64 * 100.0 / result_total as f64
} else {
100.0
};
let progress_cell: String = if ctx.open_ended() {
format!("— (open-ended, {cycles} cycles)")
} else {
let pct: f64 = ctx.progress_fraction().map(|f| f * 100.0).unwrap_or(100.0);
format!("{pct:.0}% ({cycles} of {total_extent})")
};
let rate: f64 = if elapsed > 0.0 {
consumed as f64 / elapsed
} else {
0.0
};
let rate_str = format_rate(rate);
let err_color = if errors > 0 || retries > 0 {
yellow
} else {
dim
};
let labels = ctx.subject_labels();
let depth_indent = ctx.depth_indent();
let seq_part: String = String::new();
let coords_continuation_indent = format!("{depth_indent} ");
let coords_head_consumed = depth_indent.chars().count() + 14; let coords_payload = format_coords_block(
labels,
color,
coords_head_consumed,
&coords_continuation_indent,
true,
);
let coords_line = if coords_payload.is_empty() {
String::new()
} else {
format!("\n{depth_indent} coords:{coords_payload}")
};
let _ = (bold, yellow); let chips_block = render_chips_block(&ctx.status_metric_chips(), depth_indent, dim, reset);
let errors_block =
render_outcome_errors_block(outcome_errors, errors, depth_indent, red, dim, reset);
let mut tmp = String::with_capacity(384);
let _ = write!(
&mut tmp,
"{depth_indent}{glyph_color}{glyph}{reset} {bold}{blue}[{name}]{reset}{seq}{coords}\n\
{depth_indent} status: {glyph_color}{status_label}{reset}\n\
{depth_indent} progress: {progress_cell}\n\
{depth_indent} throughput: {rate_str}\n\
{depth_indent} ok: {ok_pct:.0}% ({ok} of {result_total})\n\
{depth_indent} reliability: {err_color}e:{errors} r:{retries}{reset}\n\
{depth_indent} concurrency: {concurrency}\n\
{chips}\
{depth_indent} elapsed: {dim}{elapsed:.2}s{reset}\
{errors_block}",
name = ctx.subject_name(),
seq = seq_part,
coords = coords_line,
chips = chips_block,
status_label = outcome.label(),
errors_block = errors_block,
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_outcome_errors_block(
errors: &[crate::phase_outcome::PhaseErrorDetail],
total: u64,
indent: &str,
red: &str,
dim: &str,
reset: &str,
) -> String {
use std::fmt::Write as _;
if errors.is_empty() {
return String::new();
}
let msg_continuation = format!("{indent} ");
let detail_continuation = format!("{indent} ");
let reindent = |s: &str, prefix: &str| -> String {
let mut out = String::with_capacity(s.len() + prefix.len() * 2);
let mut first = true;
for line in s.split('\n') {
if first {
first = false;
} else {
out.push('\n');
out.push_str(prefix);
}
out.push_str(line);
}
out
};
let mut out = String::with_capacity(128);
let _ = write!(&mut out, "\n{indent} errors:");
for e in errors {
let msg = reindent(&e.message, &msg_continuation);
let _ = write!(
&mut out,
"\n{indent} {red}[{class}]{reset} {msg}",
class = e.class
);
if let Some(c) = e.cycle {
let _ = write!(&mut out, "\n{indent} {dim}cycle:{reset} {c}");
}
if let Some(t) = &e.op_template {
let t = reindent(t, &detail_continuation);
let _ = write!(&mut out, "\n{indent} {dim}op-template:{reset} {t}");
}
if let Some(r) = &e.op_resolved {
let r = reindent(r, &detail_continuation);
let _ = write!(&mut out, "\n{indent} {dim}op-resolved:{reset} {r}");
}
}
let captured = errors.len() as u64;
if captured < total {
let _ = write!(
&mut out,
"\n{indent} {dim}(+{} more occurred — not captured (buffer cap)){reset}",
total - captured
);
}
out
}
fn render_chips_block(chips: &str, indent: &str, dim: &str, reset: &str) -> String {
let entries: Vec<&str> = chips.split_whitespace().collect();
if entries.is_empty() {
return String::new();
}
let mut out = String::with_capacity(64 + entries.len() * 24);
let _ = writeln!(&mut out, "{indent} metrics:");
for chip in &entries {
let (name, value) = chip.split_once(':').unwrap_or((chip, ""));
let _ = writeln!(&mut out, "{indent} {name:<16} {dim}{value}{reset}");
}
out
}
fn render_expanded_explanation(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let green = if color { "\x1b[32m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let depth_indent = ctx.depth_indent();
let mut tmp = String::with_capacity(384);
let _ = write!(
&mut tmp,
"{depth_indent}{green}done{reset} {bold}{blue}[phase-name]{reset}\n\
{depth_indent} progress: progress% (cycles_completed of cycles_total)\n\
{depth_indent} throughput: rate (auto-scaled K/s, M/s)\n\
{depth_indent} ok: ok-pct% (ops_ok of cycles_completed)\n\
{depth_indent} reliability: e:errors r:retries\n\
{depth_indent} concurrency: fiber count\n\
{depth_indent} elapsed: {dim}wallclock seconds{reset}",
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_labeled_explanation(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let yellow = if color { "\x1b[33m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let green = if color { "\x1b[32m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let depth_indent = ctx.depth_indent();
let seq_part: String = String::new();
let coords_part: String = if ctx.subject_labels().is_empty() {
String::new()
} else {
format!(" {bold}{yellow}(scope-coords){reset}")
};
let mut tmp = String::with_capacity(160);
let _ = write!(
&mut tmp,
"{depth_indent}{green}done{reset} {seq}{bold}{blue}[phase-name]{reset}{coords} \
progress% throughput ok:ok% \
errors retries concurrency \
{dim}(elapsed){reset}",
seq = seq_part,
coords = coords_part,
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_labeled_value(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let outcome = ctx.outcome();
if outcome.is_failure() {
return render_labeled_value_failed(ctx, out);
}
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let yellow = if color { "\x1b[33m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let glyph_color = status_color(&outcome, color);
let glyph = outcome.glyph();
let cycles = ctx.cycles_completed();
let errors = ctx.errors();
let retries = ctx.retries();
let ok = ctx.ops_ok();
let skips = ctx.skips();
let concurrency = ctx.concurrency();
let elapsed = ctx.elapsed_secs();
let consumed = ctx.consumed();
let total_extent = ctx.cycles_total();
let result_total = cycles.saturating_sub(skips).max(ok);
let ok_str: String = if result_total > 0 {
format!("{:.0}%", ok as f64 * 100.0 / result_total as f64)
} else if skips > 0 {
"—".to_string()
} else {
"100%".to_string()
};
let skips_chip: String = if skips > 0 {
format!(" {dim}skip:{skips}{reset}")
} else {
String::new()
};
let meter = completion_meter(ctx);
let rate: f64 = if elapsed > 0.0 {
consumed as f64 / elapsed
} else {
0.0
};
let rate_str = format_rate(rate);
let err_color = if errors > 0 || retries > 0 {
yellow
} else {
dim
};
let labels = ctx.subject_labels();
let depth_indent = ctx.depth_indent();
let seq_part: String = String::new();
let bar = if total_extent > 0 && total_extent <= 10 {
let bg = if color { "\x1b[48;2;50;50;50m" } else { "" };
let fg = if color { "\x1b[97m" } else { "" };
format!(" {bg}{fg}{}{reset}", ballot_bar(total_extent, ok, errors))
} else {
String::new()
};
let bar_visible: usize = if total_extent > 0 && total_extent <= 10 {
1 + total_extent as usize
} else {
0
};
let name = ctx.subject_name();
let seq_visible: usize = match ctx.subject_seq() {
Some((s, t)) => format!("[{s}/{t}] ").chars().count(),
None => 0,
};
let head_consumed: usize = depth_indent.chars().count()
+ 2 + bar_visible
+ seq_visible
+ 2 + name.chars().count();
let continuation_indent = format!("{depth_indent} ");
let coords_part = format_coords_block(
labels,
color,
head_consumed,
&continuation_indent,
true,
);
let chips = ctx.status_metric_chips();
let memo = ctx.phase_memo();
let memo_row = if memo.is_empty() {
String::new()
} else {
let bold_yellow = if color { "\x1b[1;33m" } else { "" };
format!("{depth_indent} {bold_yellow}[[ {memo} ]]{reset}\n")
};
let fully_skipped = skips > 0 && cycles > 0 && skips >= cycles && ok == 0 && errors == 0;
if fully_skipped
&& crate::observer::skipped_phase_display() == crate::observer::SkippedPhaseDisplay::Mark
{
let mut tmp = String::with_capacity(160);
let _ = write!(
&mut tmp,
"{depth_indent}{dim}⊘{reset} {seq}{bold}{blue}[{name}]{reset}{coords} {dim}gated off{reset}\n\
{memo_row}\
{depth_indent} {dim}skip:{skips} c:{concurrency} ({elapsed:.2}s){reset}",
memo_row = memo_row,
depth_indent = depth_indent,
dim = dim,
reset = reset,
seq = seq_part,
bold = bold,
blue = blue,
name = ctx.subject_name(),
coords = coords_part,
skips = skips,
concurrency = concurrency,
elapsed = elapsed,
);
let len = tmp.len();
let _ = out.write_str(&tmp);
return len;
}
let mut tmp = String::with_capacity(256);
let _ = write!(
&mut tmp,
"{depth_indent}{glyph_color}{glyph}{reset}{bar} {seq}{bold}{blue}[{name}]{reset}{coords}{meter}\n\
{memo_row}\
{depth_indent} {rate_str} ok:{ok_str} \
{err_color}e:{errors} r:{retries}{reset}{skips_chip} c:{concurrency}{chips} \
{dim}({elapsed:.2}s){reset}",
depth_indent = depth_indent,
glyph_color = glyph_color,
glyph = glyph,
bar = bar,
reset = reset,
seq = seq_part,
bold = bold,
blue = blue,
name = ctx.subject_name(),
coords = coords_part,
meter = meter,
rate_str = rate_str,
ok_str = ok_str,
skips_chip = skips_chip,
err_color = err_color,
errors = errors,
retries = retries,
concurrency = concurrency,
chips = chips,
dim = dim,
elapsed = elapsed,
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn render_labeled_value_failed(ctx: &dyn ReadoutContext, out: &mut dyn ReadoutBuf) -> usize {
let color = ctx.use_color();
let bold = if color { "\x1b[1m" } else { "" };
let dim = if color { "\x1b[2m" } else { "" };
let blue = if color { "\x1b[34m" } else { "" };
let red = if color { "\x1b[31m" } else { "" };
let reset = if color { "\x1b[0m" } else { "" };
let labels = ctx.subject_labels();
let depth_indent = ctx.depth_indent();
let name = ctx.subject_name();
let seq_part: String = String::new();
let seq_visible: usize = match ctx.subject_seq() {
Some((s, t)) => format!("[{s}/{t}] ").chars().count(),
None => 0,
};
let total_extent = ctx.cycles_total();
let ok = ctx.ops_ok();
let err_count = ctx.errors();
let bar = if total_extent > 0 && total_extent <= 10 {
let bg = if color { "\x1b[48;2;50;50;50m" } else { "" };
let fg = if color { "\x1b[97m" } else { "" };
format!(
" {bg}{fg}{}{reset}",
ballot_bar(total_extent, ok, err_count)
)
} else {
String::new()
};
let bar_visible: usize = if total_extent > 0 && total_extent <= 10 {
1 + total_extent as usize
} else {
0
};
let head_consumed: usize = depth_indent.chars().count()
+ 2 + bar_visible
+ seq_visible
+ 2 + name.chars().count();
let continuation_indent = format!("{depth_indent} ");
let coords_part = format_coords_block(
labels,
color,
head_consumed,
&continuation_indent,
true,
);
let errors = ctx.outcome_errors();
let first = errors.first();
let class_label = first.map(|e| e.class.as_str()).unwrap_or("phase_failed");
let message = first
.map(|e| e.message.lines().next().unwrap_or(e.message.as_str()))
.unwrap_or("unknown error");
let elapsed = ctx.elapsed_secs();
let extra_count = err_count.saturating_sub(1);
let extra_suffix = if extra_count > 0 {
format!(" {dim}(+{extra_count} more){reset}")
} else {
String::new()
};
let mut tmp = String::with_capacity(192);
let _ = write!(
&mut tmp,
"{depth_indent}{red}✗{reset}{bar} {seq_part}{bold}{blue}[{name}]{reset}{coords_part} \
{red}{class_label}{reset}\n\
{depth_indent} {red}{message}{reset}{extra_suffix} {dim}({elapsed:.2}s){reset}",
);
let len = tmp.len();
let _ = out.write_str(&tmp);
len
}
fn format_rate(rate: f64) -> String {
if rate >= 1_000_000.0 {
format!("{:.1}M/s", rate / 1_000_000.0)
} else if rate >= 1_000.0 {
format!("{:.1}K/s", rate / 1_000.0)
} else {
format!("{:.0}/s", rate)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::readouts::buf::StringBuf;
static SERIAL_TEST_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn parse_strata_round_trips_two_strata() {
let s = parse_strata("(profile=default), (sm=OTHER, mnc=8)");
assert_eq!(s.len(), 2);
assert_eq!(
s[0].pairs,
vec![("profile".to_string(), "default".to_string())]
);
assert_eq!(
s[1].pairs,
vec![
("sm".to_string(), "OTHER".to_string()),
("mnc".to_string(), "8".to_string()),
]
);
}
#[test]
fn parse_strata_empty_input_yields_empty() {
assert_eq!(parse_strata("").len(), 0);
}
#[test]
fn strata_diff_keeps_only_changed_pairs() {
let prev = parse_strata("(profile=default), (sm=OTHER, mnc=8)");
let curr = parse_strata("(profile=default), (sm=ADA002, mnc=8)");
let d = strata_diff(&curr, &prev);
assert_eq!(d.len(), 1);
assert_eq!(d[0].pairs, vec![("sm".to_string(), "ADA002".to_string())]);
}
#[test]
fn strata_diff_empty_prior_treats_all_as_changed() {
let curr = parse_strata("(profile=default), (sm=OTHER)");
let d = strata_diff(&curr, &[]);
assert_eq!(d.len(), 2);
assert_eq!(d[0].pairs.len(), 1);
assert_eq!(d[1].pairs.len(), 1);
}
#[test]
fn format_coords_block_completed_phase_summary_elides_unchanged() {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = String::new();
}
let labels_a = "(profile=default), (sm=OTHER, mnc=8)";
let labels_b = "(profile=default), (sm=ADA002, mnc=8)";
let first = format_coords_block(
labels_a, false, 0, " ", true,
);
assert!(first.contains("profile=default"));
assert!(first.contains("sm=OTHER"));
assert!(first.contains("mnc=8"));
let second = format_coords_block(
labels_b, false, 0, " ", true,
);
assert!(
second.contains("sm=ADA002"),
"changed pair missing in second render: {second:?}"
);
assert!(
!second.contains("profile=default"),
"unchanged profile stratum should be elided: {second:?}"
);
assert!(
!second.contains("mnc=8"),
"unchanged mnc should be elided: {second:?}"
);
}
#[test]
fn format_coords_block_active_phase_shows_full_stack() {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = "(profile=default)".into();
}
let labels = "(profile=default)";
let body = format_coords_block(
labels, false, 0, " ", false,
);
assert!(
body.contains("profile=default"),
"active-phase render should show unchanged coords too: {body:?}"
);
}
#[test]
fn first_stratum_wraps_to_continuation_when_head_plus_first_overflows() {
let strata = parse_strata(
"(source_model=OTHER, maximum_node_connections=8, construction_beam_width=50)",
);
let prev: Vec<Stratum> = Vec::new();
let out = render_strata(
&strata, &prev, false, 40,
80, " ",
);
assert!(
out.starts_with('\n'),
"first-stratum overflow should start with a newline; \
got: {out:?}"
);
assert!(
out.contains("source_model=OTHER"),
"first stratum content should still appear: {out:?}"
);
assert!(
!out.starts_with(',') && !out.contains(",\n ("),
"first-stratum overflow path should not produce a comma-wrap; \
got: {out:?}"
);
}
#[test]
fn format_coords_block_active_render_does_not_advance_tracker() {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = "(profile=default), (sm=OTHER)".into();
}
let active_labels = "(profile=default), (sm=ADA002)";
for _ in 0..5 {
let _ = format_coords_block(
active_labels,
false,
0,
" ",
false,
);
let g = LAST_RENDERED_COORDS.lock().expect("lock");
assert_eq!(
g.as_str(),
"(profile=default), (sm=OTHER)",
"active-phase render must NOT advance the tracker"
);
}
let _ = format_coords_block(
active_labels,
false,
0,
" ",
true,
);
let g = LAST_RENDERED_COORDS.lock().expect("lock");
assert_eq!(
g.as_str(),
active_labels,
"completed-phase render must advance the tracker"
);
}
#[test]
fn format_coords_block_no_change_collapses_to_empty() {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = String::new();
}
let labels = "(profile=default)";
let _ = format_coords_block(labels, false, 0, " ", true);
let body = format_coords_block(labels, false, 0, " ", true);
assert_eq!(
body, "",
"no-change render should collapse to empty: {body:?}"
);
}
struct TestCtx {
phase_name: String,
phase_seq: Option<(usize, usize)>,
phase_labels: String,
cycles_completed: u64,
cycles_total: u64,
ops_ok: u64,
skips: u64,
errors: u64,
retries: u64,
concurrency: usize,
elapsed_secs: f64,
consumed: u64,
chips: String,
depth_indent: String,
use_color: bool,
outcome: crate::phase_outcome::Outcome,
outcome_errors: Vec<crate::phase_outcome::PhaseErrorDetail>,
}
impl Default for TestCtx {
fn default() -> Self {
Self {
phase_name: String::new(),
phase_seq: None,
phase_labels: String::new(),
cycles_completed: 0,
cycles_total: 0,
ops_ok: 0,
skips: 0,
errors: 0,
retries: 0,
concurrency: 0,
elapsed_secs: 0.0,
consumed: 0,
chips: String::new(),
depth_indent: String::new(),
use_color: false,
outcome: crate::phase_outcome::Outcome::completed(),
outcome_errors: Vec::new(),
}
}
}
impl ReadoutContext for TestCtx {
fn subject_name(&self) -> &str {
&self.phase_name
}
fn subject_seq(&self) -> Option<(usize, usize)> {
self.phase_seq
}
fn subject_labels(&self) -> &str {
&self.phase_labels
}
fn cycles_completed(&self) -> u64 {
self.cycles_completed
}
fn cycles_total(&self) -> u64 {
self.cycles_total
}
fn ops_ok(&self) -> u64 {
self.ops_ok
}
fn skips(&self) -> u64 {
self.skips
}
fn errors(&self) -> u64 {
self.errors
}
fn retries(&self) -> u64 {
self.retries
}
fn concurrency(&self) -> usize {
self.concurrency
}
fn elapsed_secs(&self) -> f64 {
self.elapsed_secs
}
fn consumed(&self) -> u64 {
self.consumed
}
fn status_metric_chips(&self) -> String {
self.chips.clone()
}
fn depth_indent(&self) -> &str {
&self.depth_indent
}
fn use_color(&self) -> bool {
self.use_color
}
fn event(&self) -> crate::lifecycle::EventType {
crate::lifecycle::EventType::PhaseEnd
}
fn outcome(&self) -> crate::phase_outcome::Outcome {
self.outcome.clone()
}
fn outcome_errors(&self) -> &[crate::phase_outcome::PhaseErrorDetail] {
&self.outcome_errors
}
}
fn render(ctx: &TestCtx) -> String {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = String::new();
}
let mut s = String::new();
let mut buf = StringBuf::new(&mut s);
PhaseOutcomeReadout.render(
ctx,
Lod::Labeled,
ContentMode::Value,
&ReadoutOptions::new(),
&mut buf,
);
s
}
#[test]
fn no_color_no_coords_no_chips() {
let ctx = TestCtx {
phase_name: "setup".into(),
phase_seq: Some((1, 2)),
cycles_completed: 3,
cycles_total: 3,
ops_ok: 3,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 3,
..Default::default()
};
assert_eq!(
render(&ctx),
"✓ ☑☑☑ [setup] 100%\n 300/s ok:100% e:0 r:0 c:1 (0.01s)"
);
}
#[test]
fn no_color_with_coords_and_chips() {
let ctx = TestCtx {
phase_name: "run".into(),
phase_seq: Some((1, 8)),
phase_labels: "(profile=alpha), (bucket=1, kind=READ)".into(),
cycles_completed: 162,
cycles_total: 162,
ops_ok: 162,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 162,
chips: " recall_at_10:79.62%".into(),
..Default::default()
};
assert_eq!(
render(&ctx),
"✓ [run] (profile=alpha), (bucket=1, kind=READ) 100%\n 16.2K/s ok:100% e:0 r:0 c:1 recall_at_10:79.62% (0.01s)"
);
}
fn render_at(ctx: &TestCtx, lod: Lod, mode: ContentMode) -> String {
let _serial = SERIAL_TEST_GUARD.lock().unwrap_or_else(|e| e.into_inner());
if let Ok(mut g) = LAST_RENDERED_COORDS.lock() {
*g = String::new();
}
let mut s = String::new();
let mut buf = StringBuf::new(&mut s);
PhaseOutcomeReadout.render(ctx, lod, mode, &ReadoutOptions::new(), &mut buf);
s
}
#[test]
fn render_at_isolates_each_render_from_prior_tracker() {
let ctx = TestCtx {
phase_name: "ann_query".into(),
phase_seq: Some((1, 8)),
phase_labels: "profile=alpha, k=10".into(),
cycles_completed: 100,
cycles_total: 100,
ops_ok: 99,
errors: 1,
retries: 0,
concurrency: 4,
elapsed_secs: 1.5,
consumed: 100,
chips: " recall_at_10:79.62% latency_p99:1.23ms".into(),
..Default::default()
};
let first = render_at(&ctx, Lod::Expanded, ContentMode::Value);
let second = render_at(&ctx, Lod::Expanded, ContentMode::Value);
assert!(
first.contains("profile=alpha, k=10"),
"first render should carry coords: {first}"
);
assert!(
second.contains("profile=alpha, k=10"),
"second render must NOT elide coords (tracker must reset \
per render_at call): {second}"
);
}
#[test]
fn compact_value_drops_seq_rate_counts_chips() {
let ctx = TestCtx {
phase_name: "setup".into(),
phase_seq: Some((1, 2)),
phase_labels: "(profile=alpha)".into(),
cycles_completed: 3,
cycles_total: 3,
ops_ok: 3,
errors: 0,
retries: 0,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 3,
chips: " recall_at_10:79.62%".into(),
..Default::default()
};
assert_eq!(
render_at(&ctx, Lod::Compact, ContentMode::Value),
"✓ [setup] 100% (0.01s)",
);
}
#[test]
fn compact_value_pct_zero_when_no_extent() {
let ctx = TestCtx {
phase_name: "x".into(),
cycles_completed: 0,
cycles_total: 0,
elapsed_secs: 0.5,
..Default::default()
};
assert_eq!(
render_at(&ctx, Lod::Compact, ContentMode::Value),
"✓ [x] 100% (0.50s)",
);
}
#[test]
fn compact_explanation_describes_each_field() {
let ctx = TestCtx {
phase_name: "x".into(),
..Default::default()
};
let s = render_at(&ctx, Lod::Compact, ContentMode::Explanation);
assert!(s.contains("done"), "expected 'done': {s}");
assert!(s.contains("phase-name"), "expected 'phase-name': {s}");
assert!(s.contains("progress%"), "expected 'progress%': {s}");
assert!(s.contains("(elapsed)"), "expected '(elapsed)': {s}");
assert!(
!s.contains("idx/total"),
"compact must not describe seq prefix it doesn't render: {s}"
);
assert!(
!s.contains("throughput"),
"compact must not describe throughput it doesn't render: {s}"
);
}
#[test]
fn expanded_value_emits_multi_line_block() {
let ctx = TestCtx {
phase_name: "ann_query".into(),
phase_seq: Some((1, 8)),
phase_labels: "profile=alpha, k=10".into(),
cycles_completed: 100,
cycles_total: 100,
ops_ok: 99,
errors: 1,
retries: 0,
concurrency: 4,
elapsed_secs: 1.5,
consumed: 100,
chips: " recall_at_10:79.62% latency_p99:1.23ms".into(),
..Default::default()
};
let s = render_at(&ctx, Lod::Expanded, ContentMode::Value);
assert!(s.contains("✓ [ann_query]"));
assert!(!s.contains("[1/8]"));
assert!(s.contains("profile=alpha, k=10"));
assert!(s.contains("progress: 100% (100 of 100)"));
assert!(s.contains("throughput:"));
assert!(s.contains("ok: 99% (99 of 100)"));
assert!(s.contains("reliability: e:1 r:0"));
assert!(s.contains("concurrency: 4"));
assert!(s.contains("metrics:"));
assert!(s.contains("recall_at_10"));
assert!(s.contains("latency_p99"));
assert!(s.contains("elapsed: 1.50s"));
let line_count = s.lines().count();
assert!(
line_count >= 8,
"expanded should be multi-line (got {line_count}): {s}"
);
}
#[test]
fn expanded_value_omits_metrics_block_when_no_chips() {
let ctx = TestCtx {
phase_name: "setup".into(),
cycles_completed: 1,
cycles_total: 1,
ops_ok: 1,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 1,
chips: String::new(),
..Default::default()
};
let s = render_at(&ctx, Lod::Expanded, ContentMode::Value);
assert!(
!s.contains("metrics:"),
"expected no metrics: header when chips empty: {s}"
);
}
#[test]
fn expanded_value_omits_coords_line_when_no_labels() {
let ctx = TestCtx {
phase_name: "x".into(),
phase_labels: String::new(),
cycles_completed: 1,
cycles_total: 1,
ops_ok: 1,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 1,
..Default::default()
};
let s = render_at(&ctx, Lod::Expanded, ContentMode::Value);
assert!(
!s.contains("coords:"),
"expected no coords: line when labels empty: {s}"
);
}
#[test]
fn expanded_explanation_describes_each_row() {
let ctx = TestCtx {
phase_name: "x".into(),
..Default::default()
};
let s = render_at(&ctx, Lod::Expanded, ContentMode::Explanation);
assert!(s.contains("phase-name"));
assert!(s.contains("progress:"));
assert!(s.contains("throughput:"));
assert!(s.contains("ok:"));
assert!(s.contains("reliability:"));
assert!(s.contains("concurrency:"));
assert!(s.contains("elapsed:"));
assert!(s.lines().count() >= 7);
}
#[test]
fn monotonicity_compact_subset_of_labeled() {
let ctx = TestCtx {
phase_name: "setup".into(),
cycles_completed: 3,
cycles_total: 3,
ops_ok: 3,
concurrency: 1,
elapsed_secs: 0.01,
consumed: 3,
..Default::default()
};
let labeled = render_at(&ctx, Lod::Labeled, ContentMode::Value);
let compact = render_at(&ctx, Lod::Compact, ContentMode::Value);
assert!(labeled.contains("[setup]") && compact.contains("[setup]"));
assert!(labeled.contains('✓') && compact.contains('✓'));
assert!(labeled.contains("100%") && compact.contains("100%"));
assert!(labeled.contains("(0.01s)") && compact.contains("(0.01s)"));
}
#[test]
fn explanation_mode_describes_each_field() {
let ctx = TestCtx {
phase_name: "setup".into(),
phase_seq: Some((1, 2)),
phase_labels: "(profile=alpha)".into(),
..Default::default()
};
let mut s = String::new();
let mut buf = StringBuf::new(&mut s);
let n = PhaseOutcomeReadout.render(
&ctx,
Lod::Labeled,
ContentMode::Explanation,
&ReadoutOptions::new(),
&mut buf,
);
assert!(n > 0, "explanation should render");
assert!(s.contains("done"), "expected 'done' descriptor: {s}");
assert!(s.contains("phase-name"), "expected 'phase-name': {s}");
assert!(s.contains("scope-coords"), "expected 'scope-coords': {s}");
assert!(!s.contains("idx/total"), "seq descriptor must be gone: {s}");
assert!(s.contains("progress%"), "expected 'progress%': {s}");
assert!(s.contains("throughput"), "expected 'throughput': {s}");
assert!(s.contains("ok:ok%"), "expected ok descriptor: {s}");
assert!(s.contains("(elapsed)"), "expected '(elapsed)': {s}");
}
#[test]
fn labeled_failed_uses_x_glyph_and_first_error_class() {
let ctx = TestCtx {
phase_name: "ensure_compacted".into(),
phase_labels: String::new(),
elapsed_secs: 14400.0,
outcome: crate::phase_outcome::Outcome::failed(),
outcome_errors: vec![crate::phase_outcome::PhaseErrorDetail {
class: "poll_timeout".into(),
message: "phase-poll deadline reached after 14441.3s".into(),
op_name: None,
cycle: None,
op_template: None,
op_resolved: None,
at_nanos: 0,
retryable: false,
}],
..Default::default()
};
let out = render(&ctx);
assert!(
out.starts_with("✗ "),
"failed render must start with the ✗ glyph: {out:?}"
);
assert!(
out.contains("[ensure_compacted]"),
"phase name still in line 1: {out:?}"
);
assert!(
out.contains("poll_timeout"),
"first-error class on line 1: {out:?}"
);
assert!(
out.contains("phase-poll deadline"),
"first-error message on line 2: {out:?}"
);
assert!(out.contains("(14400.00s)"), "elapsed on line 2: {out:?}");
}
#[test]
fn labeled_failed_shows_more_count_when_multiple_errors() {
let mk_err = |class: &str, msg: &str| crate::phase_outcome::PhaseErrorDetail {
class: class.into(),
message: msg.into(),
op_name: None,
cycle: None,
op_template: None,
op_resolved: None,
at_nanos: 0,
retryable: false,
};
let ctx = TestCtx {
phase_name: "p".into(),
elapsed_secs: 1.0,
errors: 3,
outcome: crate::phase_outcome::Outcome::failed(),
outcome_errors: vec![
mk_err("A", "msg-a"),
mk_err("B", "msg-b"),
mk_err("C", "msg-c"),
],
..Default::default()
};
let out = render(&ctx);
assert!(
out.contains("(+2 more)"),
"expected `(+2 more)` truncation marker: {out:?}"
);
}
#[test]
fn labeled_skipped_uses_tilde_glyph() {
let ctx = TestCtx {
phase_name: "rampup".into(),
outcome: crate::phase_outcome::Outcome::skipped(),
cycles_completed: 0,
cycles_total: 0,
elapsed_secs: 0.0,
..Default::default()
};
let out = render(&ctx);
assert!(
out.starts_with("~ "),
"skipped render uses ~ glyph: {out:?}"
);
assert!(out.contains("[rampup]"), "phase name preserved: {out:?}");
}
#[test]
fn expanded_failed_includes_per_error_block() {
let ctx = TestCtx {
phase_name: "ann_query".into(),
cycles_completed: 5,
cycles_total: 10,
elapsed_secs: 1.5,
outcome: crate::phase_outcome::Outcome::failed(),
outcome_errors: vec![crate::phase_outcome::PhaseErrorDetail {
class: "Timeout".into(),
message: "read timed out".into(),
op_name: Some("read".into()),
cycle: Some(3),
op_template: Some("SELECT * FROM ks.t WHERE k = {cycle}".into()),
op_resolved: Some("SELECT * FROM ks.t WHERE k = 3".into()),
at_nanos: 0,
retryable: true,
}],
..Default::default()
};
let s = render_at(&ctx, Lod::Expanded, ContentMode::Value);
assert!(
s.contains("✗ [ann_query]"),
"expanded header uses ✗ glyph for Failed: {s}"
);
assert!(s.contains("status:"), "status row present: {s}");
assert!(s.contains("failed"), "status label shows 'failed': {s}");
assert!(s.contains("errors:"), "errors header present: {s}");
assert!(
s.contains("[Timeout] read timed out"),
"per-error class+message: {s}"
);
assert!(
s.contains("cycle:") && s.contains(" 3"),
"cycle row present when populated: {s}"
);
assert!(s.contains("op-template:"), "op-template row present: {s}");
assert!(s.contains("op-resolved:"), "op-resolved row present: {s}");
}
#[test]
fn compact_glyph_tracks_outcome_status() {
use crate::phase_outcome::Outcome;
for (outcome, want) in [
(Outcome::completed(), '✓'),
(Outcome::failed(), '✗'),
(Outcome::completed_failed(), '✗'),
(Outcome::skipped(), '~'),
(Outcome::interrupted(), '…'),
] {
let ctx = TestCtx {
phase_name: "x".into(),
elapsed_secs: 0.1,
outcome,
..Default::default()
};
let s = render_at(&ctx, Lod::Compact, ContentMode::Value);
assert!(
s.starts_with(want),
"compact glyph for {want:?} missing: {s:?}"
);
}
}
#[test]
fn failed_small_phase_renders_ballot_bar_with_errors_first() {
let ctx = TestCtx {
phase_name: "tiny".into(),
cycles_completed: 5,
cycles_total: 5,
ops_ok: 3,
errors: 2,
elapsed_secs: 0.5,
outcome: crate::phase_outcome::Outcome::failed(),
outcome_errors: vec![crate::phase_outcome::PhaseErrorDetail {
class: "Timeout".into(),
message: "deadline exceeded".into(),
op_name: None,
cycle: None,
op_template: None,
op_resolved: None,
at_nanos: 0,
retryable: false,
}],
..Default::default()
};
let out = render(&ctx);
assert!(
out.starts_with("✗ ☒☒☑☑☑ "),
"failed ≤10 phase should lead with bar (errors first): {out:?}"
);
}
#[test]
fn err_color_promotes_when_errors_or_retries() {
let ctx = TestCtx {
phase_name: "run".into(),
phase_seq: Some((1, 1)),
cycles_completed: 10,
cycles_total: 10,
ops_ok: 9,
errors: 1,
retries: 0,
concurrency: 1,
elapsed_secs: 1.0,
consumed: 10,
use_color: true,
..Default::default()
};
let out = render(&ctx);
assert!(
out.contains("\x1b[33me:1 r:0\x1b[0m"),
"expected yellow err_color around `e:1 r:0`, got: {out:?}"
);
assert!(
out.contains('\n'),
"expected two-line break in labeled render: {out:?}"
);
}
#[test]
fn fully_skipped_phase_shows_skip_chip_not_fake_ok() {
let ctx = TestCtx {
phase_name: "recall_postcompact".into(),
cycles_completed: 10_000,
cycles_total: 10_000,
skips: 10_000,
elapsed_secs: 0.02,
consumed: 10_000,
concurrency: 20,
..TestCtx::default()
};
let out = render(&ctx);
assert!(out.contains("gated off"), "explicit skip marker: {out:?}");
assert!(out.contains('⊘'), "skip glyph shown: {out:?}");
assert!(out.contains("skip:10000"), "skip count shown: {out:?}");
assert!(
!out.contains("ok:"),
"no ok% of any kind on a gated-off phase: {out:?}"
);
assert!(!out.contains("/s"), "no rate on a gated-off phase: {out:?}");
}
}