From 3a7a28e6829949cac43b24e053c7ef8518cc9779 Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Sat, 9 May 2026 00:56:36 +0900 Subject: [PATCH] feat(core): AnswerCitation gains indexed_at + stale (fb-32) Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/kebab-core/src/answer.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/kebab-core/src/answer.rs b/crates/kebab-core/src/answer.rs index 4fc5f0c..2b9fc7c 100644 --- a/crates/kebab-core/src/answer.rs +++ b/crates/kebab-core/src/answer.rs @@ -35,6 +35,11 @@ pub struct Answer { pub struct AnswerCitation { pub marker: Option, pub citation: Citation, + /// p9-fb-32: cited doc's `documents.updated_at`. + #[serde(with = "time::serde::rfc3339")] + pub indexed_at: OffsetDateTime, + /// p9-fb-32: server-computed staleness flag per config threshold. + pub stale: bool, } /// p9-fb-15: history 가 prompt 에 들어갈 때의 한 turn. RAG facade 가 @@ -90,3 +95,29 @@ pub struct TokenUsage { #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct TraceId(pub String); + +#[cfg(test)] +mod tests { + use super::*; + use crate::asset::WorkspacePath; + use crate::citation::Citation; + use time::macros::datetime; + + #[test] + fn answer_citation_serializes_indexed_at_and_stale() { + let ac = AnswerCitation { + marker: Some("[1]".to_string()), + citation: Citation::Line { + path: WorkspacePath::new("a.md".to_string()).unwrap(), + start: 1, + end: 1, + section: None, + }, + indexed_at: datetime!(2026-05-09 12:00:00 UTC), + stale: false, + }; + let v = serde_json::to_value(&ac).unwrap(); + assert_eq!(v["indexed_at"], "2026-05-09T12:00:00Z"); + assert_eq!(v["stale"], false); + } +}