From d024fcf4a38d3b5dd45f55fa242f3970eb6b550f Mon Sep 17 00:00:00 2001 From: Sanjay Ramadugu Date: Fri, 21 Aug 2026 02:06:47 -0700 Subject: [PATCH 1/2] test(cli): pin channels list updated_at semantics for kind 39000 metadata Signed-off-by: Sanjay Ramadugu --- crates/buzz-cli/src/commands/channels.rs | 48 ++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/crates/buzz-cli/src/commands/channels.rs b/crates/buzz-cli/src/commands/channels.rs index 7ad051ef9fc..cf0ba7269c4 100644 --- a/crates/buzz-cli/src/commands/channels.rs +++ b/crates/buzz-cli/src/commands/channels.rs @@ -1197,6 +1197,7 @@ pub async fn dispatch_canvas(cmd: crate::CanvasCmd, client: &BuzzClient) -> Resu mod tests { use super::{ apply_cardinality_rule, build_template_report, cmd_set_add_policy, + extract_channel_metadata, finalize_roster_resolution, name_matches, resolve_roster_with_archive_filter, validate_ttl_seconds, validate_update_channel_fields, ArchivedExclusion, ChannelSummary, ResolvedAgent, RosterResolution, SkippedSlug, @@ -1748,3 +1749,50 @@ mod tests { ); } } + +#[cfg(test)] +mod extract_channel_metadata_tests { + use super::extract_channel_metadata; + use serde_json::json; + + #[test] + fn updated_at_carries_kind_39000_created_at() { + let ev = json!({ + "kind": 39000, + "created_at": 1787300000u64, + "tags": [ + ["d", "11111111-1111-1111-1111-111111111111"], + ["name", "general"], + ["about", "desc"] + ] + }); + let meta = extract_channel_metadata(&ev); + assert_eq!(meta.get("updated_at"), Some(&json!(1787300000u64))); + } + + #[test] + fn created_at_is_deprecated_alias_of_updated_at() { + let ev = json!({ + "kind": 39000, + "created_at": 1787300000u64, + "tags": [["d", "11111111-1111-1111-1111-111111111111"]] + }); + let meta = extract_channel_metadata(&ev); + assert_eq!( + meta.get("created_at"), + meta.get("updated_at"), + "created_at must stay pinned to updated_at until removal" + ); + } + + #[test] + fn missing_created_at_yields_zero_for_both_fields() { + let ev = json!({ + "kind": 39000, + "tags": [["d", "11111111-1111-1111-1111-111111111111"]] + }); + let meta = extract_channel_metadata(&ev); + assert_eq!(meta.get("updated_at"), Some(&json!(0u64))); + assert_eq!(meta.get("created_at"), Some(&json!(0u64))); + } +} From f7ac2b5d045b02eb9e3e9d69daa744a7903477dd Mon Sep 17 00:00:00 2001 From: Sanjay Ramadugu Date: Fri, 21 Aug 2026 02:07:18 -0700 Subject: [PATCH 2/2] fix(cli): report channels list last-write as updated_at, keep created_at as deprecated alias Signed-off-by: Sanjay Ramadugu --- crates/buzz-cli/src/commands/channels.rs | 13 +++++++++++-- crates/buzz-cli/src/lib.rs | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/buzz-cli/src/commands/channels.rs b/crates/buzz-cli/src/commands/channels.rs index cf0ba7269c4..5bbb4c2369a 100644 --- a/crates/buzz-cli/src/commands/channels.rs +++ b/crates/buzz-cli/src/commands/channels.rs @@ -13,12 +13,22 @@ use crate::commands::channel_templates::{self, ChannelTemplateRecord, TemplateAg use crate::error::CliError; use crate::validate::{parse_uuid, read_or_stdin, validate_hex64, validate_uuid}; +/// Extracts the channel summary fields from a kind 39000 channel-metadata event. +/// +/// The timestamp carried here is the time of the **last channel-metadata write**, +/// not the channel's creation: kind 39000 is addressable/replaceable, so its +/// `created_at` is overwritten on every metadata update and the channel's birth +/// time is not recoverable from it. `updated_at` names this value honestly; +/// `created_at` is a deprecated alias kept for one release so existing scripts +/// do not break silently — prefer `updated_at`. fn extract_channel_metadata(e: &serde_json::Value) -> serde_json::Value { + let updated_at = e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0); serde_json::json!({ "channel_id": extract_d_tag(e), "name": extract_tag_value(e, "name"), "description": extract_tag_value(e, "about"), - "created_at": e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0), + "updated_at": updated_at, + "created_at": updated_at, }) } @@ -1197,7 +1207,6 @@ pub async fn dispatch_canvas(cmd: crate::CanvasCmd, client: &BuzzClient) -> Resu mod tests { use super::{ apply_cardinality_rule, build_template_report, cmd_set_add_policy, - extract_channel_metadata, finalize_roster_resolution, name_matches, resolve_roster_with_archive_filter, validate_ttl_seconds, validate_update_channel_fields, ArchivedExclusion, ChannelSummary, ResolvedAgent, RosterResolution, SkippedSlug, diff --git a/crates/buzz-cli/src/lib.rs b/crates/buzz-cli/src/lib.rs index 5cac8c941e1..16bc94800cf 100644 --- a/crates/buzz-cli/src/lib.rs +++ b/crates/buzz-cli/src/lib.rs @@ -533,6 +533,10 @@ pub enum MessagesCmd { #[derive(Subcommand)] pub enum ChannelsCmd { /// List channels visible to the current identity + /// + /// Each channel's `updated_at` is the time of its last channel-metadata + /// write (kind 39000 is replaceable), not its creation time. `created_at` + /// is a deprecated alias of `updated_at`. #[command( after_help = "Examples:\n buzz channels list\n buzz channels list --visibility open" )]