use std::{cmp::Ordering, collections::HashSet};
use khive_types::VerbPresentationPolicy;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum NoteContentScope {
#[default]
None,
Record,
Items,
Notes,
}
impl NoteContentScope {
pub fn protect(self, mut value: Value, transform: impl FnOnce(Value) -> Value) -> Value {
if self == Self::None {
return transform(value);
}
fn take_content(record: &mut Value) -> Option<Value> {
record.as_object_mut()?.remove("content")
}
fn restore_content(record: &mut Value, content: Option<Value>) {
if let (Some(record), Some(content)) = (record.as_object_mut(), content) {
record.insert("content".to_string(), content);
}
}
if self == Self::Record {
let content = take_content(&mut value);
let mut value = transform(value);
restore_content(&mut value, content);
return value;
}
let key = if self == Self::Items {
"items"
} else {
"notes"
};
let contents: Vec<_> = value
.get_mut(key)
.and_then(Value::as_array_mut)
.into_iter()
.flatten()
.map(take_content)
.collect();
let mut value = transform(value);
if let Some(records) = value.get_mut(key).and_then(Value::as_array_mut) {
for (record, content) in records.iter_mut().zip(contents) {
restore_content(record, content);
}
}
value
}
fn stringify_table_content(self, value: &mut Value) {
fn stringify(record: &mut Value) {
if let Some(content) = record.get_mut("content") {
if content.is_object() || content.is_array() {
*content = Value::String(content.to_string());
}
}
}
match self {
Self::None => {}
Self::Record => stringify(value),
Self::Items | Self::Notes => {
let key = if self == Self::Items {
"items"
} else {
"notes"
};
if let Some(records) = value.get_mut(key).and_then(Value::as_array_mut) {
records.iter_mut().for_each(stringify);
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum OutputFormat {
#[default]
Json,
Auto,
Table,
}
const CELL_TRUNCATE: usize = 120;
const PROPERTY_HOIST_FIELDS: &[&str] = &["trigger_at", "due", "status"];
pub fn render_format(value: Value, format: OutputFormat, presentation: PresentationMode) -> String {
render_format_with_note_content(value, format, presentation, NoteContentScope::None)
}
pub fn render_format_with_note_content(
value: Value,
format: OutputFormat,
presentation: PresentationMode,
content_scope: NoteContentScope,
) -> String {
let value = prepare_format_value_with_note_content(value, format, presentation, content_scope);
match format {
OutputFormat::Json => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
OutputFormat::Auto if content_scope != NoteContentScope::None => {
serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string())
}
OutputFormat::Auto | OutputFormat::Table => {
render_auto(value, presentation != PresentationMode::Verbose)
}
}
}
pub fn prepare_format_value(
value: Value,
format: OutputFormat,
presentation: PresentationMode,
) -> Value {
let scope = match format {
OutputFormat::Json if presentation == PresentationMode::Agent => {
Some(RedundancyScope::Machine)
}
OutputFormat::Json => None,
OutputFormat::Auto | OutputFormat::Table if presentation != PresentationMode::Verbose => {
Some(RedundancyScope::View)
}
OutputFormat::Auto | OutputFormat::Table => None,
};
match scope {
Some(scope) => apply_redundancy_drop(value, scope),
None => value,
}
}
pub fn prepare_format_value_with_note_content(
value: Value,
format: OutputFormat,
presentation: PresentationMode,
content_scope: NoteContentScope,
) -> Value {
let mut value = content_scope.protect(value, |value| {
prepare_format_value(value, format, presentation)
});
if format == OutputFormat::Table {
content_scope.stringify_table_content(&mut value);
}
value
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RedundancyScope {
View,
Machine,
}
pub fn apply_redundancy_drop(value: Value, scope: RedundancyScope) -> Value {
match value {
Value::Object(_) => drop_record(value, scope),
Value::Array(arr) => Value::Array(
arr.into_iter()
.map(|v| {
if v.is_object() {
drop_record(v, scope)
} else {
v
}
})
.collect(),
),
other => other,
}
}
fn drop_record(value: Value, scope: RedundancyScope) -> Value {
let Value::Object(mut map) = value else {
return value;
};
if scope == RedundancyScope::View {
map.remove("full_id");
}
if map.get("namespace").and_then(Value::as_str) == Some("local") {
map.remove("namespace");
}
let props_val = map.remove("properties");
if let Some(Value::Object(props)) = props_val {
let mut new_props = Map::new();
for (k, v) in props {
let strict_round_trip = scope == RedundancyScope::Machine
&& (k == "full_id" || ROUND_TRIP_FULL_UUID_FIELDS.contains(&k.as_str()));
if !strict_round_trip && map.get(&k) == Some(&v) {
continue;
}
new_props.insert(k, v);
}
if !new_props.is_empty() {
map.insert("properties".to_string(), Value::Object(new_props));
}
} else if let Some(other) = props_val {
map.insert("properties".to_string(), other);
}
let stream_entry = is_stream_entry(&map);
let out: Map<String, Value> = map
.into_iter()
.map(|(k, v)| {
if stream_entry && k == "record" {
return (k, v);
}
let v = match v {
Value::Array(arr) => Value::Array(
arr.into_iter()
.map(|item| {
if item.is_object() {
drop_record(item, scope)
} else {
item
}
})
.collect(),
),
other => other,
};
(k, v)
})
.collect();
Value::Object(out)
}
struct RecordArray {
key: Option<String>,
records: Vec<Value>,
columns: Vec<String>,
}
fn render_auto(value: Value, truncate: bool) -> String {
match locate_record_array(&value) {
Some(found) => render_table_with_siblings(&value, &found, truncate),
None => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
}
}
fn locate_record_array(value: &Value) -> Option<RecordArray> {
let build = |key: Option<String>, arr: &[Value]| {
let records: Vec<Value> = arr.iter().cloned().map(hoist_table_scalars).collect();
let columns = collect_keys(&records);
RecordArray {
key,
records,
columns,
}
};
match value {
Value::Array(arr) if is_record_array(arr) => Some(build(None, arr)),
Value::Object(map) => map.iter().find_map(|(k, v)| match v {
Value::Array(arr) if is_record_array(arr) => Some(build(Some(k.clone()), arr)),
_ => None,
}),
_ => None,
}
}
fn hoist_table_scalars(record: Value) -> Value {
let Value::Object(mut map) = record else {
return record;
};
let mut props = match map.remove("properties") {
Some(Value::Object(props)) => props,
Some(other) => {
map.insert("properties".to_string(), other);
return Value::Object(map);
}
None => return Value::Object(map),
};
for field in PROPERTY_HOIST_FIELDS {
if map.contains_key(*field) {
continue;
}
if props
.get(*field)
.is_some_and(|v| !v.is_object() && !v.is_array())
{
let v = props.remove(*field).expect("checked above");
map.insert((*field).to_string(), v);
}
}
if !props.is_empty() {
map.insert("properties".to_string(), Value::Object(props));
}
Value::Object(map)
}
fn is_record_array(arr: &[Value]) -> bool {
arr.len() >= 2 && arr.iter().all(Value::is_object)
}
fn render_table_with_siblings(value: &Value, found: &RecordArray, truncate: bool) -> String {
let mut out = render_table(&found.records, &found.columns, truncate);
let (Value::Object(map), Some(array_key)) = (value, &found.key) else {
return out;
};
for (k, v) in map {
if k == array_key {
continue;
}
let text = match v {
Value::String(s) if !s.contains(['\n', '\r']) => s.clone(),
other => serde_json::to_string(other).unwrap_or_default(),
};
out.push_str(&format!("{k}: {text}\n"));
}
out
}
fn collect_keys(records: &[Value]) -> Vec<String> {
let mut seen = HashSet::new();
let mut keys = Vec::new();
for record in records {
if let Value::Object(map) = record {
for k in map.keys() {
if seen.insert(k.clone()) {
keys.push(k.clone());
}
}
}
}
keys
}
fn render_table(records: &[Value], keys: &[String], truncate: bool) -> String {
let mut out = String::new();
out.push('|');
for k in keys {
out.push(' ');
out.push_str(k);
out.push_str(" |");
}
out.push('\n');
out.push('|');
for _ in keys {
out.push_str("---|");
}
out.push('\n');
for record in records {
out.push('|');
for k in keys {
let cell = record.get(k).unwrap_or(&Value::Null);
let text = cell_text(k, cell, truncate);
out.push(' ');
out.push_str(&text);
out.push_str(" |");
}
out.push('\n');
}
out
}
fn truncation_exempt(key: &str) -> bool {
matches!(
key,
"id" | "kind"
| "status"
| "priority"
| "relation"
| "title"
| "name"
| "signature"
| "slug"
| "assignee"
| "from"
| "to"
| "due"
) || key.ends_with("_id")
|| key.ends_with("_at")
|| key.starts_with("due")
}
fn cell_text(key: &str, value: &Value, truncate: bool) -> String {
let raw = match value {
Value::Null => String::new(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
Value::String(s) => s.clone(),
Value::Object(_) => return "{…}".to_string(),
Value::Array(arr) => {
if arr.iter().any(|v| v.is_object() || v.is_array()) {
return "[…]".to_string();
}
serde_json::to_string(value).unwrap_or_default()
}
};
let escaped = raw.replace('|', "\\|").replace(['\n', '\r'], " ");
if !truncate || truncation_exempt(key) {
return escaped;
}
let char_count = escaped.chars().count();
if char_count > CELL_TRUNCATE {
let truncated: String = escaped.chars().take(CELL_TRUNCATE).collect();
format!("{truncated}...")
} else {
escaped
}
}
pub fn format_utc_rfc3339_seconds<Tz: chrono::TimeZone>(value: &chrono::DateTime<Tz>) -> String {
value
.with_timezone(&chrono::Utc)
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
}
pub fn micros_to_iso(micros: i64) -> String {
if let Some(dt) = chrono::DateTime::<chrono::Utc>::from_timestamp_micros(micros) {
return dt.to_rfc3339_opts(chrono::SecondsFormat::Micros, true);
}
const CYCLE_MICROS: i64 = 146_097 * 86_400 * 1_000_000;
let cycles = micros.div_euclid(CYCLE_MICROS);
let reduced =
chrono::DateTime::<chrono::Utc>::from_timestamp_micros(micros.rem_euclid(CYCLE_MICROS))
.expect("400-year remainder is within Chrono's range");
let year = i64::from(chrono::Datelike::year(&reduced)) + cycles * 400;
let rendered = reduced.to_rfc3339_opts(chrono::SecondsFormat::Micros, true);
format!("{year:+05}{}", &rendered[4..])
}
pub fn rfc3339_to_utc_micros(raw: &str) -> Result<i64, chrono::ParseError> {
chrono::DateTime::parse_from_rfc3339(raw.trim()).map(|dt| dt.timestamp_micros())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum PresentationMode {
#[default]
Agent,
Verbose,
Human,
}
const LIFECYCLE_NULL_PRESERVE: &[&str] = &[
"completed_at",
"deleted_at",
"due_at",
"read_at",
"started_at",
"superseded_at",
"applied_at",
"withdrawn_at",
"reviewed_at",
"parent_id",
"superseded_by",
"replaced_by",
"poll_interval_secs",
"stalled",
"last_success_at",
"last_poll_attempt_at",
"last_failure_at",
"last_error",
"consecutive_failures",
];
const EMPTY_ARRAY_PRESERVE: &[&str] = &[
"items",
"entities",
"notes",
"edges",
"results",
"entries",
"neighbors",
];
fn is_stable_list_envelope(map: &Map<String, Value>) -> bool {
map.contains_key("requested_limit")
&& map.contains_key("effective_limit")
&& map.contains_key("limit_clamped")
&& EMPTY_ARRAY_PRESERVE
.iter()
.any(|field| map.contains_key(*field))
}
fn is_keyset_cursor_envelope(map: &Map<String, Value>) -> bool {
map.contains_key("next_after")
&& (map.get("results").is_some_and(Value::is_array)
|| (map.get("head_seq").is_some_and(Value::is_number)
&& map.get("entries").is_some_and(Value::is_array)))
}
fn is_stream_entry(map: &Map<String, Value>) -> bool {
map.get("seq").is_some_and(Value::is_number)
&& map.contains_key("id")
&& map.contains_key("created_at")
&& map.contains_key("record")
}
const PAYLOAD_TIMESTAMP_FIELDS: &[&str] = &["trigger_at", "due"];
const ROUND_TRIP_FULL_UUID_FIELDS: &[&str] = &[
"context_entity_id",
"thread_id",
"outbound_ref",
"parent_id",
"session_id",
"project_id",
];
const SCORE_FIELDS: &[&str] = &[
"score",
"rank_score",
"vector_similarity",
"keyword_score",
"salience",
"decay_factor",
"rrf_score",
"similarity",
"cross_encoder_score",
"graph_proximity_score",
];
const UUID_CANONICAL_LEN: usize = 36;
fn should_shorten_uuid_field(key: &str) -> bool {
if key == "full_id" || key == "bridge_instance_id" || ROUND_TRIP_FULL_UUID_FIELDS.contains(&key)
{
return false;
}
key == "id" || key.ends_with("_id") || matches!(key, "superseded_by" | "replaced_by")
}
pub fn present(value: Value, mode: PresentationMode, now_unix_seconds: i64) -> Value {
present_with_policy(
value,
mode,
now_unix_seconds,
VerbPresentationPolicy::Standard,
)
}
pub fn present_with_policy(
value: Value,
mode: PresentationMode,
now_unix_seconds: i64,
policy: VerbPresentationPolicy,
) -> Value {
present_with_policy_at(value, mode, now_unix_seconds.into(), policy)
}
#[derive(Clone, Copy)]
pub struct PresentationNow {
seconds: i64,
nanoseconds: u32,
}
impl From<i64> for PresentationNow {
fn from(seconds: i64) -> Self {
Self {
seconds,
nanoseconds: 0,
}
}
}
impl From<i32> for PresentationNow {
fn from(seconds: i32) -> Self {
i64::from(seconds).into()
}
}
impl From<chrono::DateTime<chrono::Utc>> for PresentationNow {
fn from(now: chrono::DateTime<chrono::Utc>) -> Self {
Self {
seconds: now.timestamp(),
nanoseconds: now.timestamp_subsec_nanos(),
}
}
}
pub fn present_with_policy_at(
value: Value,
mode: PresentationMode,
now: PresentationNow,
policy: VerbPresentationPolicy,
) -> Value {
if policy == VerbPresentationPolicy::AlwaysVerbose {
return value;
}
match mode {
PresentationMode::Verbose | PresentationMode::Human => value,
PresentationMode::Agent => {
let mut value = value;
let agenda_next = (policy == VerbPresentationPolicy::AgendaContinuation)
.then(|| value.as_object_mut().and_then(|map| map.remove("next")))
.flatten();
let agenda_empty = policy == VerbPresentationPolicy::AgendaContinuation
&& value
.get("events")
.and_then(Value::as_array)
.is_some_and(Vec::is_empty);
let config = AgentTransformConfig {
preserved_nulls: LIFECYCLE_NULL_PRESERVE.iter().copied().collect(),
scores: SCORE_FIELDS.iter().copied().collect(),
payload_timestamps: PAYLOAD_TIMESTAMP_FIELDS.iter().copied().collect(),
now,
};
let mut value = transform_agent(
value,
&config,
AgentTreeContext {
inside_properties: false,
object_properties: false,
array_member: false,
receipt_context: match policy {
VerbPresentationPolicy::StreamAppendReceipt => ReceiptContext::AppendRoot,
VerbPresentationPolicy::StreamBatchReceipts => ReceiptContext::BatchRoot,
_ => ReceiptContext::None,
},
},
);
if let Some(map) = value.as_object_mut() {
if let Some(next) = agenda_next {
map.insert("next".into(), next);
}
if agenda_empty {
map.insert("events".into(), Value::Array(Vec::new()));
}
}
value
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ReceiptContext {
None,
AppendRoot,
BatchRoot,
BatchResults,
BatchMember,
}
struct AgentTransformConfig {
preserved_nulls: HashSet<&'static str>,
scores: HashSet<&'static str>,
payload_timestamps: HashSet<&'static str>,
now: PresentationNow,
}
#[derive(Clone, Copy)]
struct AgentTreeContext {
inside_properties: bool,
object_properties: bool,
array_member: bool,
receipt_context: ReceiptContext,
}
fn transform_agent(
value: Value,
config: &AgentTransformConfig,
context: AgentTreeContext,
) -> Value {
match value {
Value::Object(map) => {
let preserve_list_envelope =
is_stable_list_envelope(&map) || is_keyset_cursor_envelope(&map);
let stream_entry = is_stream_entry(&map);
let relative_created_at = (context.array_member
&& !context.inside_properties
&& !map.contains_key("created_at_relative")
&& !matches!(
context.receipt_context,
ReceiptContext::AppendRoot | ReceiptContext::BatchMember
))
.then(|| map.get("created_at").and_then(Value::as_str))
.flatten()
.and_then(|raw| {
let (_, created_at) = exact_timestamp(raw)?;
let age = config.now.seconds.checked_sub(created_at)?;
age.checked_sub(i64::from(
timestamp_fraction_cmp(raw, config.now.nanoseconds) == Ordering::Greater,
))
})
.filter(|difference| *difference >= 0)
.map(relative_time);
let mut out = Map::new();
for (k, v) in map {
if stream_entry && k == "record" {
out.insert(k, v);
continue;
}
if v.is_string()
&& ((context.receipt_context == ReceiptContext::AppendRoot
&& k == "created_at")
|| (context.receipt_context == ReceiptContext::BatchMember
&& matches!(k.as_str(), "created_at" | "updated_at")))
{
out.insert(k, v);
continue;
}
let child_receipt_context = if context.receipt_context == ReceiptContext::BatchRoot
&& k == "results"
&& v.is_array()
{
ReceiptContext::BatchResults
} else {
ReceiptContext::None
};
let child_inside_properties = context.inside_properties || k == "properties";
let child_object_properties =
context.object_properties || (k == "properties" && v.is_object());
let transformed = transform_field_agent(
&k,
v,
config,
AgentFieldContext {
inside_properties: child_inside_properties,
object_properties: child_object_properties,
preserve_list_envelope,
receipt_context: child_receipt_context,
},
);
match transformed {
None => {} Some(tv) => {
out.insert(k, tv);
}
}
}
if let Some(relative) = relative_created_at {
out.insert("created_at_relative".to_string(), Value::String(relative));
}
Value::Object(out)
}
Value::Array(arr) => {
let items: Vec<Value> = arr
.into_iter()
.map(|v| {
transform_agent(
v,
config,
AgentTreeContext {
inside_properties: context.inside_properties,
object_properties: context.object_properties,
array_member: true,
receipt_context: if context.receipt_context
== ReceiptContext::BatchResults
{
ReceiptContext::BatchMember
} else {
ReceiptContext::None
},
},
)
})
.collect();
Value::Array(items)
}
other => other,
}
}
#[derive(Clone, Copy)]
struct AgentFieldContext {
inside_properties: bool,
object_properties: bool,
preserve_list_envelope: bool,
receipt_context: ReceiptContext,
}
fn transform_field_agent(
key: &str,
value: Value,
config: &AgentTransformConfig,
context: AgentFieldContext,
) -> Option<Value> {
match &value {
Value::Null => {
if config.preserved_nulls.contains(key)
|| (context.preserve_list_envelope && key == "next_after")
{
Some(value)
} else {
None
}
}
Value::Array(a)
if context.preserve_list_envelope
&& a.is_empty()
&& EMPTY_ARRAY_PRESERVE.contains(&key) =>
{
Some(value)
}
Value::String(s) if s.is_empty() && context.object_properties => Some(value),
Value::String(s) if s.is_empty() => None,
Value::Array(a) if a.is_empty() => None,
Value::Object(o) if o.is_empty() => None,
Value::Number(_) if config.scores.contains(key) => {
if let Some(f) = value.as_f64() {
Some(truncate_to_3_sig_figs(f))
} else {
Some(value)
}
}
Value::String(s) if is_canonical_uuid(s) && should_shorten_uuid_field(key) => {
Some(Value::String(s[..8].to_string()))
}
Value::String(s)
if !context.inside_properties
&& !config.payload_timestamps.contains(key)
&& looks_like_iso8601(s) =>
{
Some(Value::String(
exact_timestamp(s).map_or_else(|| s.clone(), |(rendered, _)| rendered),
))
}
Value::Object(_) | Value::Array(_) => Some(transform_agent(
value,
config,
AgentTreeContext {
inside_properties: context.inside_properties,
object_properties: context.object_properties,
array_member: false,
receipt_context: context.receipt_context,
},
)),
_ => Some(value),
}
}
fn is_canonical_uuid(s: &str) -> bool {
if s.len() != UUID_CANONICAL_LEN {
return false;
}
let b = s.as_bytes();
b[8] == b'-'
&& b[13] == b'-'
&& b[18] == b'-'
&& b[23] == b'-'
&& b[..8].iter().all(|c| c.is_ascii_hexdigit())
&& b[9..13].iter().all(|c| c.is_ascii_hexdigit())
&& b[14..18].iter().all(|c| c.is_ascii_hexdigit())
&& b[19..23].iter().all(|c| c.is_ascii_hexdigit())
&& b[24..].iter().all(|c| c.is_ascii_hexdigit())
}
fn looks_like_iso8601(s: &str) -> bool {
if s.len() < 16 {
return false;
}
let b = s.as_bytes();
b[4] == b'-'
&& b[7] == b'-'
&& b[10] == b'T'
&& b[13] == b':'
&& b[..4].iter().all(|c| c.is_ascii_digit())
&& b[5..7].iter().all(|c| c.is_ascii_digit())
&& b[8..10].iter().all(|c| c.is_ascii_digit())
&& b[11..13].iter().all(|c| c.is_ascii_digit())
}
fn exact_timestamp(s: &str) -> Option<(String, i64)> {
let unix = parse_iso8601_unix(s)?;
let tail = s.get(19..)?;
let fraction = if let Some(after_dot) = tail.strip_prefix('.') {
let digits = after_dot.bytes().take_while(u8::is_ascii_digit).count();
tail.get(..digits + 1)?
} else {
""
};
let utc = chrono::DateTime::<chrono::Utc>::from_timestamp(unix, 0)?;
Some((
format!("{}{fraction}Z", utc.format("%Y-%m-%dT%H:%M:%S")),
unix,
))
}
fn timestamp_fraction_cmp(timestamp: &str, now_nanoseconds: u32) -> Ordering {
let fraction = if timestamp.as_bytes().get(19) == Some(&b'.') {
timestamp.as_bytes()[20..]
.split(|byte| !byte.is_ascii_digit())
.next()
.unwrap_or_default()
} else {
&[]
};
let now_fraction = format!("{now_nanoseconds:09}");
for index in 0..fraction.len().max(9) {
let created_digit = fraction.get(index).copied().unwrap_or(b'0');
let now_digit = now_fraction.as_bytes().get(index).copied().unwrap_or(b'0');
match created_digit.cmp(&now_digit) {
Ordering::Equal => {}
difference => return difference,
}
}
Ordering::Equal
}
fn parse_iso8601_unix(s: &str) -> Option<i64> {
if s.len() < 20 {
return None;
}
let b = s.as_bytes();
if b[4] != b'-' || b[7] != b'-' || b[10] != b'T' || b[13] != b':' || b[16] != b':' {
return None;
}
let year: i64 = parse_digits(&b[0..4])?;
let month: i64 = parse_digits(&b[5..7])?;
let day: i64 = parse_digits(&b[8..10])?;
let hour: i64 = parse_digits(&b[11..13])?;
let minute: i64 = parse_digits(&b[14..16])?;
let second: i64 = parse_digits(&b[17..19])?;
let local = chrono::NaiveDate::from_ymd_opt(
year.try_into().ok()?,
month.try_into().ok()?,
day.try_into().ok()?,
)?
.and_hms_opt(
hour.try_into().ok()?,
minute.try_into().ok()?,
second.try_into().ok()?,
)?
.and_utc()
.timestamp();
let offset_secs = parse_tz_offset_secs(s.get(19..)?)?;
local.checked_sub(offset_secs)
}
fn parse_tz_offset_secs(tail: &str) -> Option<i64> {
let mut rest = tail;
if let Some(after_dot) = rest.strip_prefix('.') {
let frac_len = after_dot.bytes().take_while(u8::is_ascii_digit).count();
if frac_len == 0 {
return None;
}
rest = &after_dot[frac_len..];
}
if rest == "Z" {
return Some(0);
}
let sign: i64 = match rest.as_bytes().first()? {
b'+' => 1,
b'-' => -1,
_ => return None,
};
let digits = &rest[1..];
let (hh, mm) = match digits.len() {
5 if digits.as_bytes()[2] == b':' => (
parse_digits(&digits.as_bytes()[0..2])?,
parse_digits(&digits.as_bytes()[3..5])?,
),
4 => (
parse_digits(&digits.as_bytes()[0..2])?,
parse_digits(&digits.as_bytes()[2..4])?,
),
_ => return None,
};
if hh > 23 || mm > 59 {
return None;
}
Some(sign * (hh * 3600 + mm * 60))
}
fn parse_digits(b: &[u8]) -> Option<i64> {
let s = std::str::from_utf8(b).ok()?;
s.parse().ok()
}
fn relative_time(diff_secs: i64) -> String {
if diff_secs < 60 {
format!("{diff_secs}s ago")
} else if diff_secs < 3600 {
format!("{}m ago", diff_secs / 60)
} else if diff_secs < 86400 {
format!("{}h ago", diff_secs / 3600)
} else {
format!("{}d ago", diff_secs / 86400)
}
}
fn truncate_to_3_sig_figs(f: f64) -> Value {
if f == 0.0 || !f.is_finite() {
return Value::from(f);
}
let magnitude = f.abs().log10().floor() as i32;
let factor = 10f64.powi(2 - magnitude);
let rounded = (f * factor).round() / factor;
serde_json::Number::from_f64(rounded)
.map(Value::Number)
.unwrap_or(Value::from(rounded))
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
const NOW: i64 = 1_748_016_480;
#[test]
fn utc_rfc3339_seconds_normalizes_offsets_and_omits_fractions() {
for (raw, expected) in [
(
"2026-01-01T00:15:30.987654321+01:00",
"2025-12-31T23:15:30Z",
),
("1969-12-31T23:59:59.999999999Z", "1969-12-31T23:59:59Z"),
] {
let instant = chrono::DateTime::parse_from_rfc3339(raw).unwrap();
assert_eq!(format_utc_rfc3339_seconds(&instant), expected, "{raw}");
}
}
#[test]
fn micros_to_iso_preserves_chrono_range_spelling() {
for (micros, expected) in [
(0, "1970-01-01T00:00:00.000000Z"),
(-1, "1969-12-31T23:59:59.999999Z"),
(1_748_016_480_123_456, "2025-05-23T16:08:00.123456Z"),
(-62_198_755_200_000_000, "-0001-01-01T00:00:00.000000Z"),
(253_402_300_800_000_000, "+10000-01-01T00:00:00.000000Z"),
(-8_334_601_228_800_000_000, "-262143-01-01T00:00:00.000000Z"),
(8_210_266_876_799_999_999, "+262142-12-31T23:59:59.999999Z"),
] {
assert!(
chrono::DateTime::<chrono::Utc>::from_timestamp_micros(micros).is_some(),
"{micros}"
);
assert_eq!(micros_to_iso(micros), expected, "{micros}");
}
}
#[test]
fn micros_to_iso_formats_outside_chrono_range_exactly() {
for (micros, expected) in [
(i64::MIN, "-290308-12-21T19:59:05.224192Z"),
(i64::MAX, "+294247-01-10T04:00:54.775807Z"),
(-8_334_601_228_800_000_001, "-262144-12-31T23:59:59.999999Z"),
(8_210_266_876_800_000_000, "+262143-01-01T00:00:00.000000Z"),
(-8_898_108_636_303_876_544, "-280000-02-29T12:34:56.123456Z"),
(8_776_940_198_400_000_001, "+280100-03-01T00:00:00.000001Z"),
] {
assert!(
chrono::DateTime::<chrono::Utc>::from_timestamp_micros(micros).is_none(),
"{micros}"
);
assert_eq!(micros_to_iso(micros), expected, "{micros}");
}
}
#[test]
fn expanded_micros_timestamps_pass_through_agent_without_relative_labels() {
for (micros, expected) in [
(i64::MIN, "-290308-12-21T19:59:05.224192Z"),
(i64::MAX, "+294247-01-10T04:00:54.775807Z"),
] {
let shown = present(
json!({"items": [{"created_at": micros_to_iso(micros)}]}),
PresentationMode::Agent,
NOW,
);
assert_eq!(shown, json!({"items": [{"created_at": expected}]}));
assert!(rfc3339_to_utc_micros(expected).is_err());
}
}
#[test]
fn rfc3339_to_utc_micros_round_trips_and_rejects_partial_forms() {
let micros = 1_748_016_480_000_000_i64;
assert_eq!(rfc3339_to_utc_micros(µs_to_iso(micros)), Ok(micros));
assert_eq!(
rfc3339_to_utc_micros(" 2025-05-23T12:08:00-04:00 "),
Ok(micros)
);
assert!(rfc3339_to_utc_micros("2026-05-23").is_err());
assert!(rfc3339_to_utc_micros("2026-05-23T16:18:00").is_err());
}
fn agent(v: Value) -> Value {
present(v, PresentationMode::Agent, NOW)
}
#[test]
fn agent_preserves_empty_string_under_properties() {
let v = json!({
"id": "a1b2c3d4",
"summary": "",
"properties": {"k": "", "nested": {"deep": ""}},
});
let out = agent(v);
assert_eq!(out["properties"]["k"], json!(""));
assert_eq!(out["properties"]["nested"]["deep"], json!(""));
assert!(out.get("summary").is_none());
}
#[test]
fn agent_drops_scalar_empty_properties_value() {
let v = json!({
"id": "a1b2c3d4",
"properties": "",
});
let out = agent(v);
assert!(out.get("properties").is_none());
}
#[test]
fn agent_still_drops_empty_containers_under_properties() {
let v = json!({
"id": "a1b2c3d4",
"properties": {"tags": [], "meta": {}, "kept": "x"},
});
let out = agent(v);
assert!(out["properties"].get("tags").is_none());
assert!(out["properties"].get("meta").is_none());
assert_eq!(out["properties"]["kept"], json!("x"));
}
#[test]
fn verbose_passthrough() {
let v = json!({"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "X"});
let out = present(v.clone(), PresentationMode::Verbose, NOW);
assert_eq!(out, v);
}
#[test]
fn agent_shortens_uuid() {
let v = json!({"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"});
let out = agent(v);
assert_eq!(out["id"], json!("a1b2c3d4"));
}
#[test]
fn agent_drops_empty_string() {
let v = json!({"title": "ok", "description": ""});
let out = agent(v);
assert!(out.get("description").is_none());
assert_eq!(out["title"], json!("ok"));
}
#[test]
fn agent_drops_empty_array() {
let v = json!({"tags": [], "title": "ok"});
let out = agent(v);
assert!(out.get("tags").is_none());
}
#[test]
fn agent_preserves_empty_list_page_arrays() {
let v = json!({
"items": [],
"entities": [],
"notes": [],
"edges": [],
"results": [],
"entries": [],
"neighbors": [],
"next_after": null,
"requested_limit": 10,
"effective_limit": 10,
"limit_clamped": false,
});
let out = agent(v);
for key in EMPTY_ARRAY_PRESERVE {
assert_eq!(out[*key], json!([]), "missing structural key {key}");
}
assert_eq!(out["next_after"], json!(null));
}
#[test]
fn agent_preserves_keyset_cursor_envelope_completion_signals() {
let out = agent(json!({
"results": [],
"limit": 500,
"order": "created_at ASC, id ASC",
"next_after": null,
}));
assert_eq!(out["results"], json!([]));
assert!(
out.get("next_after").is_some_and(Value::is_null),
"terminal cursor page must keep next_after:null: {out}"
);
}
#[test]
fn agent_results_without_next_after_is_not_a_cursor_envelope() {
let out = agent(json!({"results": [], "limit": 500, "title": "ordinary response"}));
assert!(out.get("results").is_none());
}
#[test]
fn agent_still_drops_empty_arrays_outside_list_envelopes() {
let out = agent(json!({"items": [], "entities": [], "title": "ordinary response"}));
assert!(out.get("items").is_none());
assert!(out.get("entities").is_none());
}
#[test]
fn agent_drops_empty_object() {
let v = json!({"properties": {}, "title": "ok"});
let out = agent(v);
assert!(out.get("properties").is_none());
}
#[test]
fn agent_drops_non_lifecycle_null() {
let v = json!({"result": null, "title": "ok"});
let out = agent(v);
assert!(out.get("result").is_none());
}
#[test]
fn agent_preserves_lifecycle_null() {
let v = json!({"completed_at": null, "due_at": null, "title": "ok"});
let out = agent(v);
assert_eq!(out["completed_at"], json!(null));
assert_eq!(out["due_at"], json!(null));
}
#[test]
fn agent_preserves_relationship_null() {
let v = json!({"parent_id": null, "superseded_by": null});
let out = agent(v);
assert_eq!(out["parent_id"], json!(null));
assert_eq!(out["superseded_by"], json!(null));
}
#[test]
fn agent_preserves_unknown_channel_heartbeat_nulls() {
let v = json!({
"channels": [{
"poll_interval_secs": null,
"stalled": null,
"last_success_at": null,
"last_poll_attempt_at": null,
"last_failure_at": null,
"last_error": null,
"consecutive_failures": null,
"quarantined_count": 1,
}]
});
let out = agent(v);
let channel = out["channels"][0].as_object().expect("channel object");
for field in [
"poll_interval_secs",
"stalled",
"last_success_at",
"last_poll_attempt_at",
"last_failure_at",
"last_error",
"consecutive_failures",
] {
assert_eq!(
channel.get(field),
Some(&Value::Null),
"Agent presentation must preserve unknown heartbeat fact `{field}`"
);
}
}
#[test]
fn agent_truncates_score_field() {
let v = json!({"score": 0.12345678});
let out = agent(v);
let s = out["score"].as_f64().unwrap();
assert!((s - 0.123).abs() < 1e-9, "expected ~0.123, got {s}");
}
#[test]
fn agent_presents_ranking_alias_and_nested_evidence() {
let canonical = json!([
{
"rank_score": 0.0325224749,
"score": 0.0325224749,
"rank_score_kind": "rrf",
"signals": {"vector_similarity": 0.842567, "keyword_score": 18.375}
},
{"rank_score": 0.0, "score": 0.0, "rank_score_kind": "keyword", "signals": {}}
]);
for mode in [PresentationMode::Verbose, PresentationMode::Human] {
assert_eq!(present(canonical.clone(), mode, NOW), canonical);
}
let shown = agent(canonical);
assert_eq!(shown[0]["rank_score"], json!(0.0325));
assert_eq!(shown[0]["score"], shown[0]["rank_score"]);
assert_eq!(shown[0]["rank_score_kind"], "rrf");
assert_eq!(shown[0]["signals"]["vector_similarity"], json!(0.843));
assert_eq!(shown[0]["signals"]["keyword_score"], json!(18.4));
assert_eq!(shown[1]["rank_score"], json!(0.0));
assert_eq!(shown[1]["score"], shown[1]["rank_score"]);
assert_eq!(shown[1]["rank_score_kind"], "keyword");
assert!(shown[1].get("signals").is_none());
}
#[test]
fn agent_renders_old_timestamp_exactly() {
let v = json!({"created_at": "2020-01-01T10:30:45.123456Z"});
let out = agent(v);
assert_eq!(out["created_at"], json!("2020-01-01T10:30:45.123456Z"));
assert!(out.get("created_at_relative").is_none());
}
#[test]
fn agent_renders_recent_timestamp_exactly() {
let ts_unix = NOW - 180;
let ts = unix_to_iso8601(ts_unix);
let v = json!({"updated_at": ts.clone()});
let out = agent(v);
assert_eq!(out["updated_at"], json!(ts));
}
#[test]
fn agent_does_not_compact_top_level_trigger_at_field() {
let at = "2026-07-11T19:00:00-04:00";
let v = json!({
"id": "a1b2c3d4",
"full_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event_type": "remind",
"trigger_at": at,
"repeat": null,
"status": "pending",
});
let out = agent(v);
assert_eq!(out["trigger_at"], json!(at));
}
#[test]
fn agent_does_not_compact_top_level_trigger_at_utc() {
let at = "2026-07-11T23:00:00Z";
let v = json!({"trigger_at": at});
let out = agent(v);
assert_eq!(out["trigger_at"], json!(at));
}
#[test]
fn agent_does_not_compact_top_level_trigger_at_offset_less() {
let at = "2026-07-11T23:00:00";
let v = json!({"trigger_at": at});
let out = agent(v);
assert_eq!(out["trigger_at"], json!(at));
}
#[test]
fn agent_still_renders_other_top_level_timestamps_alongside_trigger_at() {
let v = json!({
"trigger_at": "2026-07-11T19:00:00-04:00",
"created_at": "2020-01-01T10:30:45.123456Z",
});
let out = agent(v);
assert_eq!(out["trigger_at"], json!("2026-07-11T19:00:00-04:00"));
assert_eq!(out["created_at"], json!("2020-01-01T10:30:45.123456Z"));
}
#[test]
fn agent_does_not_compact_top_level_due() {
let due = "2026-08-01T09:30:15-04:00";
let v = json!({"due": due});
let out = agent(v);
assert_eq!(out["due"], json!(due));
}
#[test]
fn agent_still_protects_nested_trigger_at_under_properties() {
let at = "2026-07-11T19:00:00-04:00";
let v = json!({
"id": "a1b2c3d4",
"properties": {"trigger_at": at, "status": "pending"},
});
let out = agent(v);
assert_eq!(out["properties"]["trigger_at"], json!(at));
}
#[test]
fn agent_recurses_into_nested_objects() {
let v = json!({
"items": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tags": [],
"score": 0.9999
}
]
});
let out = agent(v);
let item = &out["items"][0];
assert_eq!(item["id"], json!("a1b2c3d4"));
assert!(item.get("tags").is_none());
let s = item["score"].as_f64().unwrap();
assert!((s - 1.0).abs() < 1e-9);
}
#[test]
fn agent_preserves_full_id_as_36_chars() {
let uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
let v = json!({"id": uuid, "full_id": uuid, "title": "X"});
let out = agent(v);
assert_eq!(
out["id"],
json!("a1b2c3d4"),
"id should be 8-char short form"
);
assert_eq!(
out["full_id"].as_str().unwrap().len(),
36,
"full_id must be 36 chars in agent mode"
);
assert_eq!(
out["full_id"],
json!(uuid),
"full_id must equal the original UUID"
);
assert!(
out["full_id"]
.as_str()
.unwrap()
.starts_with(out["id"].as_str().unwrap()),
"full_id must start with the short id prefix"
);
}
#[test]
fn is_canonical_uuid_recognizes_valid() {
assert!(is_canonical_uuid("a1b2c3d4-e5f6-7890-abcd-ef1234567890"));
assert!(!is_canonical_uuid("a1b2c3d4"));
assert!(!is_canonical_uuid("not-a-uuid-at-all-here---------"));
}
#[test]
fn looks_like_iso8601_recognizes_valid() {
assert!(looks_like_iso8601("2026-05-23T16:18:15.234567Z"));
assert!(!looks_like_iso8601("not a timestamp"));
assert!(!looks_like_iso8601("2026-05-23"));
}
fn unix_to_iso8601(unix: i64) -> String {
let (y, mo, d, h, mi, s) = unix_to_civil(unix);
format!("{y:04}-{mo:02}-{d:02}T{h:02}:{mi:02}:{s:02}Z")
}
fn unix_to_civil(unix: i64) -> (i64, i64, i64, i64, i64, i64) {
let s = unix % 86400;
let days = unix / 86400;
let h = s / 3600;
let m = (s % 3600) / 60;
let sec = s % 60;
let z = days + 719468;
let era = z.div_euclid(146097);
let doe = z - era * 146097;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let mo = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if mo <= 2 { y + 1 } else { y };
(y, mo, d, h, m, sec)
}
#[test]
fn agent_does_not_shorten_uuid_shaped_content_fields() {
let uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
let out = agent(json!({
"id": uuid,
"full_id": uuid,
"content": uuid,
"description": uuid,
"title": uuid,
"query": uuid,
}));
assert_eq!(out["id"], json!("a1b2c3d4"));
assert_eq!(out["full_id"], json!(uuid));
assert_eq!(out["content"], json!(uuid));
assert_eq!(out["description"], json!(uuid));
assert_eq!(out["title"], json!(uuid));
assert_eq!(out["query"], json!(uuid));
}
#[test]
fn agent_shortens_suffix_id_fields() {
let uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
let out = agent(json!({
"note_id": uuid,
"source_id": uuid,
"target_id": uuid,
}));
assert_eq!(out["note_id"], json!("a1b2c3d4"));
assert_eq!(out["source_id"], json!("a1b2c3d4"));
assert_eq!(out["target_id"], json!("a1b2c3d4"));
}
#[test]
fn agent_preserves_strict_round_trip_uuid_fields() {
let uuid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
let out = agent(json!({
"context_entity_id": uuid,
"thread_id": uuid,
"session_id": uuid,
"project_id": uuid,
"properties": {
"context_entity_id": uuid,
"thread_id": uuid,
"outbound_ref": uuid,
"parent_id": uuid,
"session_id": uuid,
"project_id": uuid,
},
"parent_id": uuid,
}));
assert_eq!(out["context_entity_id"], json!(uuid));
assert_eq!(out["thread_id"], json!(uuid));
assert_eq!(out["session_id"], json!(uuid));
assert_eq!(out["project_id"], json!(uuid));
assert_eq!(out["properties"]["context_entity_id"], json!(uuid));
assert_eq!(out["properties"]["thread_id"], json!(uuid));
assert_eq!(out["properties"]["outbound_ref"], json!(uuid));
assert_eq!(out["properties"]["parent_id"], json!(uuid));
assert_eq!(out["properties"]["session_id"], json!(uuid));
assert_eq!(out["properties"]["project_id"], json!(uuid));
assert_eq!(out["parent_id"], json!(uuid));
}
#[test]
fn format_verbose_json_preserves_full_shape() {
let v = json!({
"full_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"namespace": "local",
"properties": {"k": "v"},
"title": "test"
});
let rendered = render_format(v.clone(), OutputFormat::Json, PresentationMode::Verbose);
let parsed: Value = serde_json::from_str(&rendered).unwrap();
assert!(
parsed.get("full_id").is_some(),
"json mode must keep full_id"
);
assert_eq!(
parsed.get("namespace").and_then(Value::as_str),
Some("local")
);
assert!(parsed.get("properties").is_some());
}
#[test]
fn format_agent_json_drops_redundancy() {
let v = json!({
"full_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"namespace": "local",
"title": "test",
"status": "next",
"properties": {"status": "next", "additive": true}
});
let rendered = render_format(v, OutputFormat::Json, PresentationMode::Agent);
let parsed: Value = serde_json::from_str(&rendered).unwrap();
assert!(
parsed.get("full_id").is_some(),
"Agent JSON must keep full_id: it is the machine contract's chaining handle"
);
assert!(parsed.get("namespace").is_none());
assert_eq!(parsed["status"], json!("next"));
assert!(parsed["properties"].get("status").is_none());
assert_eq!(parsed["properties"]["additive"], json!(true));
}
#[test]
fn agent_json_keeps_full_id_while_auto_still_drops_it() {
let v = json!({
"full_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"namespace": "local",
"title": "test",
"properties": {"title": "test", "additive": true}
});
let json_rendered = render_format(v.clone(), OutputFormat::Json, PresentationMode::Agent);
let json_parsed: Value = serde_json::from_str(&json_rendered).unwrap();
assert!(
json_parsed.get("full_id").is_some(),
"Agent JSON must keep full_id"
);
assert!(
json_parsed.get("namespace").is_none(),
"Agent JSON must elide namespace=\"local\""
);
assert!(
json_parsed["properties"].get("title").is_none(),
"Agent JSON must dedupe a duplicate properties entry"
);
assert_eq!(json_parsed["properties"]["additive"], json!(true));
let auto_rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
!auto_rendered.contains("full_id"),
"Auto (view scope) must still drop full_id: {auto_rendered}"
);
assert!(
!auto_rendered.contains("\"namespace\""),
"Auto must still elide namespace=\"local\": {auto_rendered}"
);
}
#[test]
fn format_auto_homogeneous_array_renders_markdown_table() {
let v = json!([
{"id": "abc", "title": "First"},
{"id": "def", "title": "Second"}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(rendered.starts_with('|'), "must start with |");
assert!(
rendered.contains("| id |") || rendered.contains("| id"),
"must have id column"
);
assert!(rendered.contains("title"), "must have title column");
assert!(rendered.contains("|---|"), "must have separator row");
assert!(rendered.contains("abc"), "must have first row data");
assert!(rendered.contains("Second"), "must have second row data");
}
#[test]
fn format_auto_single_record_renders_compact_json() {
let v = json!({"id": "abc", "title": "Hello World"});
let rendered = render_format(v.clone(), OutputFormat::Auto, PresentationMode::Agent);
let parsed: Value = serde_json::from_str(&rendered).expect("must be valid JSON");
assert_eq!(parsed, v, "single record must round-trip losslessly");
assert!(
!rendered.starts_with('|'),
"single record must not be a markdown table"
);
}
#[test]
fn format_auto_single_record_large_payload_survives_whole() {
let briefing = "line one\nline two\n".repeat(500); let v = json!({"id": "abc", "markdown": briefing.clone()});
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
let parsed: Value = serde_json::from_str(&rendered).expect("must be valid JSON");
assert_eq!(
parsed.get("markdown").and_then(Value::as_str),
Some(briefing.as_str()),
"large payload field must survive untruncated"
);
}
#[test]
fn format_auto_table_preserves_sibling_scalars() {
let v = json!({
"results": [
{"id": "abc", "title": "First"},
{"id": "def", "title": "Second"}
],
"has_more": true,
"offset": 20,
"page_size": 2,
"truncated": false
});
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(rendered.contains("| id"), "records must render as a table");
assert!(
rendered.contains("has_more: true"),
"has_more sibling must survive: {rendered}"
);
assert!(
rendered.contains("offset: 20"),
"offset sibling must survive"
);
assert!(
rendered.contains("page_size: 2"),
"page_size sibling must survive"
);
assert!(
rendered.contains("truncated: false"),
"truncated sibling must survive"
);
}
#[test]
fn format_auto_nested_cell_renders_elision_marker() {
let big_nested: Value = json!({"k": "v".repeat(300), "other": {"deep": true}});
let v = json!([
{"id": "abc", "properties": big_nested},
{"id": "def", "properties": {"x": 1}}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
rendered.contains("{…}"),
"nested object cell must render the elision marker: {rendered}"
);
assert!(
!rendered.contains("..."),
"no truncated pseudo-JSON in nested cells"
);
}
#[test]
fn format_auto_scalar_array_cell_renders_compact_json() {
let v = json!([
{"id": "abc", "tags": ["lesson", "khive"]},
{"id": "def", "tags": ["adr"]}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
rendered.contains(r#"["lesson","khive"]"#),
"scalar array cell must be compact JSON: {rendered}"
);
}
#[test]
fn format_auto_identity_columns_not_truncated() {
let long_title = "T".repeat(300);
let long_note = "N".repeat(300);
let v = json!([
{"id": "abc", "title": long_title.clone(), "note": long_note.clone()},
{"id": "def", "title": "short", "note": "short"}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
rendered.contains(&long_title),
"title column must not be truncated"
);
assert!(
!rendered.contains(&long_note),
"non-exempt column must still truncate"
);
}
#[test]
fn format_auto_verbose_disables_truncation() {
let long_note = "N".repeat(300);
let v = json!([
{"id": "abc", "note": long_note.clone()},
{"id": "def", "note": "short"}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Verbose);
assert!(
rendered.contains(&long_note),
"verbose must render full cell content"
);
}
#[test]
fn table_path_hoists_payload_scalars_to_columns() {
let record = |n: u32| {
json!({
"id": format!("evt-{n}"),
"properties": {
"trigger_at": "2026-09-01T14:00:00-04:00",
"status": "pending",
"dispatch_receipt": {"state": "succeeded"}
}
})
};
let out = render_format(
json!([record(1), record(2)]),
OutputFormat::Auto,
PresentationMode::Agent,
);
let header = out.lines().next().expect("table header");
assert!(
header.contains("trigger_at") && header.contains("status"),
"hoisted scalars must appear as table columns, got header: {header}"
);
assert!(
out.contains("2026-09-01T14:00:00-04:00") && out.contains("pending"),
"hoisted values must render in cells:\n{out}"
);
}
#[test]
fn redundancy_drop_no_longer_hoists_outside_tables() {
let v = json!({
"id": "abc",
"properties": {
"trigger_at": "2026-09-01T14:00:00-04:00",
"status": "pending"
}
});
let reduced = apply_redundancy_drop(v, RedundancyScope::View);
assert!(
reduced.get("trigger_at").is_none() && reduced.get("status").is_none(),
"the pre-pass must not hoist; the hoist is table-path only"
);
let props = reduced.get("properties").expect("properties must remain");
assert_eq!(
props.get("status").and_then(Value::as_str),
Some("pending"),
"fallback shape keeps payload fields inside properties"
);
}
#[test]
fn sibling_string_with_newline_renders_as_json_literal() {
let v = json!({
"items": [{"id": "a", "kind": "x"}, {"id": "b", "kind": "y"}],
"note": "line one\nforged_key: forged_value",
"has_more": true
});
let out = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
!out.contains("\nforged_key: forged_value"),
"raw newline from a sibling string must not start a new line:\n{out}"
);
assert!(
out.contains(r#"note: "line one\nforged_key: forged_value""#),
"newline-bearing sibling renders as its JSON literal:\n{out}"
);
assert!(out.contains("has_more: true"), "siblings still preserved");
}
#[test]
fn carriage_return_in_cell_collapses_like_newline() {
let v = json!([
{"id": "a", "note": "before\rafter"},
{"id": "b", "note": "one\r\ntwo"}
]);
let out = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
!out.contains('\r'),
"no raw carriage return may survive into rendered output:\n{out:?}"
);
assert!(
out.contains("before after") && out.contains("one two"),
"\\r and \\r\\n collapse to spaces inside cells:\n{out}"
);
}
#[test]
fn redundancy_drop_hoist_does_not_overwrite_top_level() {
let v = json!({
"id": "abc",
"status": "active",
"properties": {"status": "pending"}
});
let reduced = apply_redundancy_drop(v, RedundancyScope::View);
assert_eq!(
reduced.get("status").and_then(Value::as_str),
Some("active"),
"existing top-level status must win"
);
assert_eq!(
reduced
.get("properties")
.and_then(|p| p.get("status"))
.and_then(Value::as_str),
Some("pending"),
"conflicting properties value must stay where it was"
);
}
#[test]
fn format_auto_scalar_fallback_compact_json() {
let v = json!(42);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert_eq!(rendered, "42");
}
#[test]
fn format_table_forces_markdown_when_array() {
let v = json!({
"items": [
{"name": "A", "score": 1},
{"name": "B", "score": 2}
]
});
let rendered = render_format(v, OutputFormat::Table, PresentationMode::Agent);
assert!(
rendered.contains("|"),
"table format must produce markdown table"
);
assert!(rendered.contains("name"), "must have name column");
assert!(rendered.contains("score"), "must have score column");
}
#[test]
fn format_table_falls_back_to_json_when_no_array() {
let v = json!({"single": "value"});
let rendered = render_format(v, OutputFormat::Table, PresentationMode::Agent);
let parsed: Value = serde_json::from_str(&rendered).unwrap();
assert_eq!(parsed["single"], json!("value"));
}
#[test]
fn format_auto_verbose_skips_redundancy_drop() {
let v = json!({
"full_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"namespace": "local",
"title": "test"
});
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Verbose);
assert!(
rendered.contains("full_id"),
"verbose must preserve full_id"
);
assert!(
rendered.contains("namespace"),
"verbose must preserve namespace"
);
}
#[test]
fn redundancy_drop_does_not_corrupt_error_shape() {
let v = json!({"ok": false, "error": "something failed", "namespace": "local"});
let reduced = apply_redundancy_drop(v.clone(), RedundancyScope::View);
assert!(
reduced.get("error").is_some(),
"redundancy drop must preserve error field"
);
assert_eq!(
reduced.get("ok").and_then(Value::as_bool),
Some(false),
"redundancy drop must preserve ok=false"
);
}
#[test]
fn redundancy_drop_properties_dedup() {
let v = json!({
"id": "abc",
"title": "Same",
"properties": {
"title": "Same", "extra": "unique" }
});
let reduced = apply_redundancy_drop(v, RedundancyScope::View);
let props = reduced.get("properties").expect("properties must remain");
assert!(props.get("extra").is_some(), "unique property must be kept");
assert!(
props.get("title").is_none(),
"duplicate top-level property must be removed"
);
}
#[test]
fn cell_text_truncates_long_values() {
let long = "X".repeat(200);
let v = json!([
{"col": long.clone()},
{"col": "short"}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
rendered.contains("..."),
"long cell must be truncated with ..."
);
assert!(
!rendered.contains(&long),
"full long string must not appear in table"
);
}
#[test]
fn cell_text_truncation_is_utf8_safe() {
let prefix = "a".repeat(119);
let suffix = "ä¸".repeat(10); let long_multibyte = format!("{prefix}{suffix}trailing");
let v = json!([
{"col": long_multibyte.clone()},
{"col": "ok"}
]);
let rendered = render_format(v, OutputFormat::Auto, PresentationMode::Agent);
assert!(
rendered.contains("..."),
"multibyte cell must be truncated with ..."
);
assert!(
std::str::from_utf8(rendered.as_bytes()).is_ok(),
"rendered output must be valid UTF-8"
);
}
#[test]
fn parse_iso8601_unix_negative_offset_matches_equivalent_utc() {
assert_eq!(
parse_iso8601_unix("2026-07-09T11:55:00-04:00"),
parse_iso8601_unix("2026-07-09T15:55:00Z")
);
}
#[test]
fn parse_iso8601_unix_positive_offset_matches_equivalent_utc() {
assert_eq!(
parse_iso8601_unix("2026-05-23T20:15:00+04:00"),
parse_iso8601_unix("2026-05-23T16:15:00Z")
);
}
#[test]
fn parse_iso8601_unix_zero_offset_matches_z() {
assert_eq!(
parse_iso8601_unix("2026-07-09T15:55:00+00:00"),
parse_iso8601_unix("2026-07-09T15:55:00Z")
);
}
#[test]
fn parse_iso8601_unix_compact_offset_form_matches_colon_form() {
assert_eq!(
parse_iso8601_unix("2026-07-09T11:55:00-0400"),
parse_iso8601_unix("2026-07-09T11:55:00-04:00")
);
}
#[test]
fn parse_iso8601_unix_fractional_seconds_with_offset() {
assert_eq!(
parse_iso8601_unix("2026-07-09T11:55:00.123-04:00"),
parse_iso8601_unix("2026-07-09T15:55:00Z")
);
}
#[test]
fn parse_iso8601_unix_fractional_seconds_with_z() {
assert_eq!(
parse_iso8601_unix("2026-07-09T15:55:00.999Z"),
parse_iso8601_unix("2026-07-09T15:55:00Z")
);
}
#[test]
fn parse_iso8601_unix_rejects_bare_form() {
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00"), None);
}
#[test]
fn parse_iso8601_unix_malformed_tail_returns_none() {
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00X"), None);
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00+04"), None);
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00."), None);
}
#[test]
fn parse_iso8601_unix_out_of_range_offset_returns_none() {
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00+24:00"), None);
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00+2400"), None);
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00+01:60"), None);
assert_eq!(parse_iso8601_unix("2026-07-09T15:55:00+0160"), None);
}
#[test]
fn parse_iso8601_unix_max_valid_offset_boundary_is_accepted() {
assert!(parse_iso8601_unix("2026-07-09T15:55:00+23:59").is_some());
assert!(parse_iso8601_unix("2026-07-09T15:55:00-23:59").is_some());
assert!(parse_iso8601_unix("2026-07-09T15:55:00+2359").is_some());
}
#[test]
fn exact_timestamp_converts_future_offset_without_relative_form() {
let out = exact_timestamp("2025-05-23T16:08:00-02:00").unwrap();
assert_eq!(out.0, "2025-05-23T18:08:00Z");
assert!(out.1 > NOW);
}
#[test]
fn exact_timestamp_converts_past_offset_and_retains_fraction() {
let out = exact_timestamp("2025-05-23T20:05:00.123+04:00").unwrap();
assert_eq!(out.0, "2025-05-23T16:05:00.123Z");
assert_eq!(out.1, NOW - 180);
}
}
#[cfg(test)]
mod stream_presentation_tests {
use super::*;
use serde_json::json;
#[test]
fn stream_agent_json_preserves_opaque_record_and_empty_page() {
let record = json!([{"id": "aabbccdd-1234-4321-1234-abcdefabcdef", "score": 0.123456789, "created_at": "2026-09-08T00:00:00.123456Z", "empty": [], "null": null, "namespace": "local", "properties": {"namespace": "local"}}]);
for payload in [record, Value::Null, json!([]), json!({}), json!("")] {
let page = json!({"entries": [{"seq": 1, "id": "aabbccdd-1234-4321-1234-abcdefabcdef", "created_at": "2026-09-08T00:00:00.123456Z", "record": payload}], "head_seq": 1, "next_after": null});
let presented = present(page, PresentationMode::Agent, 0);
let out = prepare_format_value(presented, OutputFormat::Json, PresentationMode::Agent);
assert_eq!(out["entries"][0].get("record"), Some(&payload));
assert!(out.get("next_after").is_some_and(Value::is_null));
}
let empty = json!({"entries": [], "head_seq": 0, "next_after": null});
assert_eq!(present(empty.clone(), PresentationMode::Agent, 0), empty);
}
}
#[cfg(test)]
mod issue_2537_standard_tests {
use super::*;
use serde_json::json;
#[test]
fn issue_2537_standard_context_baseline_controls() {
let timestamp = "2026-01-01T00:00:00.123456Z";
let now = 1_767_225_780; let uuid = "aabbccdd-1234-4321-1234-abcdefabcdef";
let payload = json!({"id":uuid,"created_at":timestamp,"empty":[],"null":null});
let value = json!({"tool":"stream.batch","policy":"StreamBatchReceipts","id":uuid,
"updated_at":timestamp,"score":0.123456,"empty":[],"null":null,
"results":[{"id":uuid,"version":1,"updated_at":timestamp,"details":{"updated_at":timestamp}}],
"properties":{"created_at":timestamp,"id":uuid,"empty":""},"trigger_at":timestamp,"due":timestamp,
"entry":{"seq":1,"id":uuid,"created_at":timestamp,"record":payload},
"cursor":{"head_seq":0,"entries":[],"next_after":null}});
let shown = present(value.clone(), PresentationMode::Agent, now);
assert_eq!(shown["updated_at"], timestamp);
assert_eq!(shown["results"][0]["updated_at"], timestamp);
assert_eq!(shown["results"][0]["details"]["updated_at"], timestamp);
assert_eq!(shown["id"], "aabbccdd");
assert_eq!(shown["score"], json!(0.123));
assert!(shown.get("empty").is_none());
assert!(shown.get("null").is_none());
assert_eq!(
shown["properties"],
json!({"created_at":timestamp,"id":"aabbccdd","empty":""})
);
assert_eq!(shown["trigger_at"], timestamp);
assert_eq!(shown["due"], timestamp);
assert_eq!(shown["entry"]["record"], payload);
assert_eq!(shown["entry"]["created_at"], timestamp);
assert_eq!(shown["cursor"], value["cursor"]);
for mode in [PresentationMode::Verbose, PresentationMode::Human] {
assert_eq!(present(value.clone(), mode, now), value);
}
assert_eq!(
present(
json!({"properties":timestamp}),
PresentationMode::Agent,
now
)["properties"],
timestamp
);
assert_eq!(
present(
json!({"properties":[timestamp,{"created_at":timestamp}]}),
PresentationMode::Agent,
now
)["properties"],
json!([timestamp,{"created_at":timestamp}])
);
}
}
#[cfg(test)]
mod issue_2537_receipt_policy_tests {
use super::*;
use serde_json::json;
const TIMESTAMP: &str = "2026-01-01T00:00:00.123456Z";
const NOW: i64 = 1_767_225_780;
const UUID: &str = "aabbccdd-1234-4321-1234-abcdefabcdef";
#[test]
fn issue_2537_receipt_policies_preserve_only_the_named_fields() {
let append = json!({
"id": UUID, "created_at": TIMESTAMP, "updated_at": TIMESTAMP,
"score": 0.123456, "empty": [], "null": null,
"details": {"created_at": TIMESTAMP},
"ticker": {"last_tick_at": TIMESTAMP},
});
let batch = json!({
"id": UUID, "created_at": TIMESTAMP, "updated_at": TIMESTAMP,
"results": [{
"id": UUID, "created_at": TIMESTAMP, "updated_at": TIMESTAMP,
"details": {"created_at": TIMESTAMP, "updated_at": TIMESTAMP},
"record": {"updated_at": TIMESTAMP},
"score": 0.123456, "empty": [], "null": null,
}],
"properties": {"created_at": TIMESTAMP, "id": UUID, "empty": ""},
"entry": {"seq": 1, "id": UUID, "created_at": TIMESTAMP,
"record": {"id": UUID, "updated_at": TIMESTAMP, "empty": [], "null": null}},
"trigger_at": TIMESTAMP, "due": TIMESTAMP,
"ticker": {"last_tick_at": TIMESTAMP},
});
for (value, policy) in [
(append, VerbPresentationPolicy::StreamAppendReceipt),
(batch, VerbPresentationPolicy::StreamBatchReceipts),
] {
let mut expected = present(value.clone(), PresentationMode::Agent, NOW);
assert_eq!(expected["created_at"], TIMESTAMP);
assert_eq!(expected["updated_at"], TIMESTAMP);
assert_eq!(expected["id"], "aabbccdd");
assert_eq!(expected["ticker"]["last_tick_at"], TIMESTAMP);
if policy == VerbPresentationPolicy::StreamAppendReceipt {
expected["created_at"] = json!(TIMESTAMP);
} else {
expected["results"][0]["created_at"] = json!(TIMESTAMP);
expected["results"][0]["updated_at"] = json!(TIMESTAMP);
expected["results"][0]
.as_object_mut()
.unwrap()
.remove("created_at_relative");
}
assert_eq!(
present_with_policy(value.clone(), PresentationMode::Agent, NOW, policy),
expected
);
for mode in [PresentationMode::Verbose, PresentationMode::Human] {
assert_eq!(present_with_policy(value.clone(), mode, NOW, policy), value);
}
assert_eq!(
present_with_policy(
value.clone(),
PresentationMode::Agent,
NOW,
VerbPresentationPolicy::AlwaysVerbose
),
value
);
}
}
#[test]
fn issue_2537_receipt_paths_do_not_match_other_containers_or_descendants() {
let row = json!({"created_at": TIMESTAMP, "updated_at": TIMESTAMP});
for (policy, cases) in [
(
VerbPresentationPolicy::StreamAppendReceipt,
vec![
json!([row.clone()]),
json!({"details": row.clone()}),
json!({"results": [row.clone()]}),
json!({"created_at": {"created_at": TIMESTAMP}}),
],
),
(
VerbPresentationPolicy::StreamBatchReceipts,
vec![
json!([{"results": [row.clone()]}]),
json!({"results": row.clone()}),
json!({"results": [[row.clone()]]}),
json!({"details": {"results": [row.clone()]}}),
json!({"results": [{"details": row.clone(), "record": row.clone(), "results": [row.clone()]}]}),
json!({"results": [{"created_at": {"updated_at": TIMESTAMP}, "updated_at": null}]}),
],
),
] {
for value in cases {
assert_eq!(
present_with_policy(value.clone(), PresentationMode::Agent, NOW, policy),
present(value.clone(), PresentationMode::Agent, NOW),
"container must not acquire receipt protection: {value}"
);
}
}
}
#[test]
fn issue_2537_receipt_strings_are_preserved_without_parsing() {
for text in ["", "not a timestamp", "2026-01-01T05:30:00.123456+05:30"] {
let append = json!({"created_at": text});
assert_eq!(
present_with_policy(
append.clone(),
PresentationMode::Agent,
NOW,
VerbPresentationPolicy::StreamAppendReceipt
),
append
);
let batch = json!({"results": [{"created_at": text, "updated_at": text}]});
assert_eq!(
present_with_policy(
batch.clone(),
PresentationMode::Agent,
NOW,
VerbPresentationPolicy::StreamBatchReceipts
),
batch
);
}
for non_string in [Value::Null, json!([]), json!({}), json!(42), json!(true)] {
for (value, policy) in [
(
json!({"created_at": non_string}),
VerbPresentationPolicy::StreamAppendReceipt,
),
(
json!({"results": [{"created_at": non_string, "updated_at": non_string}]}),
VerbPresentationPolicy::StreamBatchReceipts,
),
] {
assert_eq!(
present_with_policy(value.clone(), PresentationMode::Agent, NOW, policy),
present(value, PresentationMode::Agent, NOW)
);
}
}
}
}
#[cfg(test)]
mod parsed_note_content_tests {
use super::*;
use serde_json::json;
#[test]
fn issue2757_content_is_opaque_to_metadata_and_format_reductions() {
let payload = json!([
{"id":"11111111-1111-4111-8111-111111111111", "full_id":"keep", "namespace":"local",
"created_at":"2026-09-15T12:34:56.123456Z", "score":0.123456789,
"nil":null, "empty":"", "array":[], "object":{},
"properties":{"id":"11111111-1111-4111-8111-111111111111"}},
{"status":"pending"}
]);
for (scope, response, pointer) in [
(
NoteContentScope::Record,
json!({"id":"22222222-2222-4222-8222-222222222222", "content":payload}),
"/content",
),
(
NoteContentScope::Items,
json!({"items":[{"id":"22222222-2222-4222-8222-222222222222", "content":payload}], "requested_limit":1, "effective_limit":1, "limit_clamped":false}),
"/items/0/content",
),
(
NoteContentScope::Notes,
json!({"notes":[{"id":"22222222-2222-4222-8222-222222222222", "content":payload}], "next_after":null}),
"/notes/0/content",
),
] {
let presented =
scope.protect(response, |value| present(value, PresentationMode::Agent, 0));
for format in [OutputFormat::Json, OutputFormat::Auto, OutputFormat::Table] {
let rendered = render_format_with_note_content(
presented.clone(),
format,
PresentationMode::Agent,
scope,
);
let actual: Value = serde_json::from_str(&rendered)
.expect("single note stays a JSON record, never a table of its body");
let expected = if format == OutputFormat::Table {
Value::String(payload.to_string())
} else {
payload.clone()
};
assert_eq!(actual.pointer(pointer), Some(&expected));
}
}
}
}
#[cfg(test)]
mod adr045_amendment9_tests {
use super::*;
use serde_json::json;
const NOW: i64 = 1_748_016_480;
#[test]
fn agent_exact_utc_preserves_fraction_digits_and_instant() {
for (utc_clock, plus_clock, minus_clock) in [
(
"2025-05-23T16:05:00",
"2025-05-23T20:05:00",
"2025-05-23T12:05:00",
),
(
"2020-01-01T10:30:45",
"2020-01-01T14:30:45",
"2020-01-01T06:30:45",
),
] {
for fraction in ["", ".123", ".123456"] {
let expected = format!("{utc_clock}{fraction}Z");
for (clock, offset) in [
(utc_clock, "Z"),
(utc_clock, "+00:00"),
(plus_clock, "+0400"),
(minus_clock, "-04:00"),
] {
let input = format!("{clock}{fraction}{offset}");
let shown = present(
json!({"updated_at": input.clone()}),
PresentationMode::Agent,
NOW,
);
assert_eq!(shown["updated_at"], expected, "{input}");
assert_eq!(
parse_iso8601_unix(&input),
parse_iso8601_unix(&expected),
"{input}"
);
}
}
}
}
#[test]
fn agent_preserves_timestamp_prefix_body() {
let body = "2025-05-23T16:05:00Z followed by ordinary message text";
let shown = present(json!({"content": body}), PresentationMode::Agent, NOW);
assert_eq!(shown["content"], body);
}
#[test]
fn agent_preserves_offsetless_timestamp() {
let body = "2025-05-23T16:05:00";
let shown = present(json!({"content": body}), PresentationMode::Agent, NOW);
assert_eq!(shown["content"], body);
}
#[test]
fn agent_preserves_malformed_offset() {
let body = "2025-05-23T16:05:00+25:00";
let shown = present(json!({"content": body}), PresentationMode::Agent, NOW);
assert_eq!(shown["content"], body);
}
#[test]
fn agent_list_rows_pair_exact_and_relative_time_in_each_band() {
let bands = [
(42, "42s ago"),
(180, "3m ago"),
(3 * 3600, "3h ago"),
(2 * 86400, "2d ago"),
];
let rows: Vec<Value> = bands
.into_iter()
.map(|(age, _)| {
let created = chrono::DateTime::<chrono::Utc>::from_timestamp(NOW - age, 0)
.unwrap()
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
json!({"created_at": created, "updated_at": created})
})
.collect();
let shown = present(json!({"items": rows.clone()}), PresentationMode::Agent, NOW);
for (index, (_, relative)) in bands.into_iter().enumerate() {
assert_eq!(
shown["items"][index]["created_at"],
rows[index]["created_at"]
);
assert_eq!(
shown["items"][index]["updated_at"],
rows[index]["updated_at"]
);
assert_eq!(shown["items"][index]["created_at_relative"], relative);
assert!(shown["items"][index].get("updated_at_relative").is_none());
}
}
#[test]
fn agent_relative_sibling_obeys_array_properties_existing_and_future_bounds() {
let recent = "2025-05-23T16:05:00+00:00";
let future = "2025-05-23T16:08:10Z";
let shown = present(
json!({
"created_at": recent,
"items": [
{"created_at": future},
{"created_at": recent, "created_at_relative": "canonical"},
{"created_at": "2025-05-23T16:05:00"},
{"updated_at": recent},
{"created_at": "2025-05-23T16:08:00.000001Z"},
{"created_at": "2025-05-23T16:07:59.999999Z"},
{"created_at": "2025-05-23T16:08:00.000000Z"},
{"properties": [{"created_at": recent, "empty": ""}]}
],
"properties": {"nested": [{"created_at": recent}]}
}),
PresentationMode::Agent,
NOW,
);
assert_eq!(shown["created_at"], "2025-05-23T16:05:00Z");
assert!(shown.get("created_at_relative").is_none());
assert_eq!(shown["items"][0]["created_at"], future);
assert!(shown["items"][0].get("created_at_relative").is_none());
assert_eq!(shown["items"][1]["created_at_relative"], "canonical");
assert_eq!(shown["items"][2]["created_at"], "2025-05-23T16:05:00");
assert!(shown["items"][2].get("created_at_relative").is_none());
assert!(shown["items"][3].get("created_at_relative").is_none());
assert!(shown["items"][4].get("created_at_relative").is_none());
assert_eq!(shown["items"][5]["created_at_relative"], "0s ago");
assert_eq!(shown["items"][6]["created_at_relative"], "0s ago");
assert_eq!(shown["items"][7]["properties"][0]["created_at"], recent);
assert!(shown["items"][7]["properties"][0]
.get("created_at_relative")
.is_none());
assert!(shown["items"][7]["properties"][0].get("empty").is_none());
assert_eq!(shown["properties"]["nested"][0]["created_at"], recent);
assert!(shown["properties"]["nested"][0]
.get("created_at_relative")
.is_none());
}
#[test]
fn agent_relative_sibling_uses_sampled_fraction_within_same_second() {
let sampled = chrono::DateTime::parse_from_rfc3339("2025-05-23T16:08:00.500000000Z")
.unwrap()
.with_timezone(&chrono::Utc);
let shown = present_with_policy_at(
json!({"items": [
{"created_at": "2025-05-23T16:08:00.499999999Z"},
{"created_at": "2025-05-23T16:08:00.500000000Z"},
{"created_at": "2025-05-23T16:08:00.5000000000Z"},
{"created_at": "2025-05-23T16:08:00.5000000001Z"},
{"created_at": "2025-05-23T16:08:00.500000001Z"},
{"created_at": "2025-05-23T16:07:59.500000001Z"},
{"created_at": "2025-05-23T16:07:59.500000000Z"}
]}),
PresentationMode::Agent,
sampled.into(),
VerbPresentationPolicy::Standard,
);
for index in [0, 1, 2, 5] {
assert_eq!(shown["items"][index]["created_at_relative"], "0s ago");
}
for index in [3, 4] {
assert!(shown["items"][index].get("created_at_relative").is_none());
}
assert_eq!(shown["items"][6]["created_at_relative"], "1s ago");
}
}