From cc41adabb583a45f5a27119fcd2c99d5b73c14a1 Mon Sep 17 00:00:00 2001
From: th-kim0823
Date: Sat, 9 May 2026 02:17:15 +0900
Subject: [PATCH] feat(wire): search_hit.v1 + citation.v1 require indexed_at +
stale (fb-32)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Additive minor — schema_version unchanged. Existing v1 consumers
that ignore unknown fields stay compatible; consumers that validate
strictly will reject pre-fb-32 payloads, which matches the wire
contract escape hatch (recipient version >= producer required).
Cross-task placeholders: kebab-eval / kebab-tui synthetic test
fixtures pin UNIX_EPOCH + stale=false (same pattern as
hybrid.rs / vector.rs). These don't exercise staleness — Task 11
adds dedicated TUI staleness rendering tests.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
crates/kebab-eval/src/metrics.rs | 7 +++++++
crates/kebab-eval/tests/metrics_and_compare.rs | 4 ++++
crates/kebab-tui/tests/ask.rs | 4 ++++
crates/kebab-tui/tests/search.rs | 4 ++++
docs/wire-schema/v1/citation.schema.json | 6 ++++--
docs/wire-schema/v1/search_hit.schema.json | 8 ++++++--
6 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/crates/kebab-eval/src/metrics.rs b/crates/kebab-eval/src/metrics.rs
index 7a709e1..1528e23 100644
--- a/crates/kebab-eval/src/metrics.rs
+++ b/crates/kebab-eval/src/metrics.rs
@@ -444,6 +444,10 @@ mod tests {
index_version: IndexVersion(format!("idx@{rank}")),
embedding_model: None,
chunker_version: ChunkerVersion("test@1".into()),
+ // fb-32: synthetic eval fixtures don't exercise staleness;
+ // pin UNIX_EPOCH + stale=false so hits stay deterministic.
+ indexed_at: OffsetDateTime::UNIX_EPOCH,
+ stale: false,
}
}
@@ -479,6 +483,9 @@ mod tests {
end: 1,
section: None,
},
+ // fb-32: synthetic eval citations don't exercise staleness.
+ indexed_at: OffsetDateTime::UNIX_EPOCH,
+ stale: false,
}).collect(),
grounded,
refusal_reason: None,
diff --git a/crates/kebab-eval/tests/metrics_and_compare.rs b/crates/kebab-eval/tests/metrics_and_compare.rs
index 3cf992b..06cb78c 100644
--- a/crates/kebab-eval/tests/metrics_and_compare.rs
+++ b/crates/kebab-eval/tests/metrics_and_compare.rs
@@ -82,6 +82,10 @@ fn hit(rank: u32, chunk_id: &str, doc_id: &str) -> SearchHit {
index_version: IndexVersion("idx@1".into()),
embedding_model: None,
chunker_version: ChunkerVersion("test@1".into()),
+ // fb-32: synthetic eval fixtures don't exercise staleness;
+ // pin UNIX_EPOCH + stale=false so hits stay deterministic.
+ indexed_at: OffsetDateTime::UNIX_EPOCH,
+ stale: false,
}
}
diff --git a/crates/kebab-tui/tests/ask.rs b/crates/kebab-tui/tests/ask.rs
index 0f6093e..5b9e30c 100644
--- a/crates/kebab-tui/tests/ask.rs
+++ b/crates/kebab-tui/tests/ask.rs
@@ -42,6 +42,10 @@ fn make_answer(grounded: bool, refusal: Option, body: &str) -> An
end: 14,
section: Some("Section A".into()),
},
+ // fb-32: TUI ask test fixture pinned to UNIX_EPOCH + stale=false;
+ // staleness rendering covered in dedicated tests (Task 11).
+ indexed_at: OffsetDateTime::UNIX_EPOCH,
+ stale: false,
}],
grounded,
refusal_reason: refusal,
diff --git a/crates/kebab-tui/tests/search.rs b/crates/kebab-tui/tests/search.rs
index d213dc4..b76416c 100644
--- a/crates/kebab-tui/tests/search.rs
+++ b/crates/kebab-tui/tests/search.rs
@@ -51,6 +51,10 @@ fn make_hit(rank: u32, path: &str, snippet: &str, citation: Citation) -> SearchH
index_version: IndexVersion("v1".into()),
embedding_model: Some(EmbeddingModelId("multilingual-e5-small".into())),
chunker_version: ChunkerVersion("md-heading-v1".into()),
+ // fb-32: TUI search test fixtures pinned to UNIX_EPOCH + stale=false;
+ // staleness rendering covered in dedicated tests (Task 11).
+ indexed_at: time::OffsetDateTime::UNIX_EPOCH,
+ stale: false,
}
}
diff --git a/docs/wire-schema/v1/citation.schema.json b/docs/wire-schema/v1/citation.schema.json
index 90ebe0f..30ef875 100644
--- a/docs/wire-schema/v1/citation.schema.json
+++ b/docs/wire-schema/v1/citation.schema.json
@@ -4,7 +4,7 @@
"title": "Citation v1",
"description": "Stub schema — declares the schema_version label and the always-present fields. Variant-discriminated property validation lands in a later phase.",
"type": "object",
- "required": ["schema_version", "kind", "path", "uri"],
+ "required": ["schema_version", "kind", "path", "uri", "indexed_at", "stale"],
"properties": {
"schema_version": { "const": "citation.v1" },
"kind": { "enum": ["line", "page", "region", "caption", "time"] },
@@ -14,6 +14,8 @@
"page": { "type": "object" },
"region": { "type": "object" },
"caption": { "type": "object" },
- "time": { "type": "object" }
+ "time": { "type": "object" },
+ "indexed_at": { "type": "string", "format": "date-time" },
+ "stale": { "type": "boolean" }
}
}
diff --git a/docs/wire-schema/v1/search_hit.schema.json b/docs/wire-schema/v1/search_hit.schema.json
index 01b8a96..1083104 100644
--- a/docs/wire-schema/v1/search_hit.schema.json
+++ b/docs/wire-schema/v1/search_hit.schema.json
@@ -16,7 +16,9 @@
"citation",
"retrieval",
"index_version",
- "chunker_version"
+ "chunker_version",
+ "indexed_at",
+ "stale"
],
"properties": {
"schema_version": { "const": "search_hit.v1" },
@@ -33,6 +35,8 @@
"retrieval": { "type": "object" },
"index_version": { "type": "string" },
"embedding_model": { "type": ["string", "null"] },
- "chunker_version": { "type": "string" }
+ "chunker_version": { "type": "string" },
+ "indexed_at": { "type": "string", "format": "date-time" },
+ "stale": { "type": "boolean" }
}
}