Skip to main content

relay_knowledge/domain/knowledge/
file_index.rs

1//! Technology-neutral contracts for bounded local file indexing and retrieval.
2
3use serde::{Deserialize, Serialize};
4
5use crate::domain::{EvidenceSpan, IndexKind, IndexState};
6
7/// Authorized root scanned by the local file indexer.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct FileIndexRoot {
10    pub scope_id: String,
11    pub root_id: String,
12    pub root_path: String,
13}
14
15/// Indexed file-location record produced by a bounded scan.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct FileIndexEntry {
18    pub scope_id: String,
19    pub root_id: String,
20    pub path: String,
21    pub relative_path: String,
22    pub file_name: String,
23    pub extension: Option<String>,
24    pub parent_dir: String,
25    pub size_bytes: u64,
26    pub modified_at_ms: u64,
27    pub fingerprint: String,
28}
29
30/// Full-root replacement produced by one scan pass.
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct FileIndexRootUpdate {
33    pub root: FileIndexRoot,
34    pub entries: Vec<FileIndexEntry>,
35    pub processed_content_paths: std::collections::BTreeSet<String>,
36    pub content_entries: Vec<FileContentEntry>,
37    pub scan_error_count: usize,
38    pub truncated: bool,
39    pub content_truncated: bool,
40    pub content_read_error_count: usize,
41    pub last_error: Option<String>,
42    pub now_ms: u64,
43}
44
45/// Root-level status surfaced to health and service diagnostics.
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct FileIndexRootStatus {
48    pub scope_id: String,
49    pub root_id: String,
50    pub root_path: String,
51    pub indexed_file_count: usize,
52    pub missing_file_count: usize,
53    pub scan_error_count: usize,
54    pub truncated: bool,
55    #[serde(default)]
56    pub content_truncated: bool,
57    #[serde(default)]
58    pub content_read_error_count: usize,
59    pub indexed_content_count: usize,
60    pub skipped_content_count: usize,
61    pub unchanged_content_count: usize,
62    pub stale_content_cursor_count: usize,
63    pub last_indexed_at_ms: Option<u64>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub last_error: Option<String>,
66}
67
68/// Summary returned after one or more roots are indexed.
69#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
70pub struct FileIndexScanSummary {
71    pub root_count: usize,
72    pub indexed_file_count: usize,
73    pub missing_file_count: usize,
74    pub indexed_content_count: usize,
75    pub skipped_content_count: usize,
76    pub unchanged_content_count: usize,
77    pub stale_content_cursor_count: usize,
78    pub scan_error_count: usize,
79    pub content_read_error_count: usize,
80    pub truncated_root_count: usize,
81    pub roots: Vec<FileIndexRootStatus>,
82}
83
84/// Bounded file-location query.
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct FileSearchRequest {
87    pub query: String,
88    pub source_scope: Option<String>,
89    pub root_id: Option<String>,
90    pub limit: usize,
91    pub timeout_ms: u64,
92}
93
94/// One file-location search hit.
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct FileSearchHit {
97    pub scope_id: String,
98    pub root_id: String,
99    pub path: String,
100    pub relative_path: String,
101    pub file_name: String,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub extension: Option<String>,
104    pub parent_dir: String,
105    pub size_bytes: u64,
106    pub modified_at_ms: u64,
107    pub status: String,
108    pub rank: usize,
109    pub score: f64,
110}
111
112/// Text chunk extracted from an authorized local file.
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct FileContentChunk {
115    pub chunk_index: usize,
116    pub start_byte: u32,
117    pub end_byte: u32,
118    pub start_line: u32,
119    pub end_line: u32,
120    pub content: String,
121}
122
123/// Content read-model record produced by a bounded scan.
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct FileContentEntry {
126    pub scope_id: String,
127    pub root_id: String,
128    pub path: String,
129    pub relative_path: String,
130    pub fingerprint: String,
131    pub content_hash: String,
132    pub indexed_at_ms: u64,
133    pub graph_version: u64,
134    pub chunks: Vec<FileContentChunk>,
135    pub skipped_reason: Option<String>,
136}
137
138/// Scoped derived cursor state for one file-content read-model input.
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140pub struct FileContentReadModelCursor {
141    pub kind: IndexKind,
142    pub source_scope: String,
143    pub root_id: String,
144    pub path: String,
145    pub content_hash: String,
146    pub indexed_graph_version: u64,
147    pub state: IndexState,
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub stale_reason: Option<String>,
150}
151
152/// Deterministic candidate fact extracted from file content.
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct FileKnowledgeFactCandidate {
155    pub candidate_id: String,
156    pub kind: String,
157    pub subject: String,
158    pub predicate: String,
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub object: Option<String>,
161    pub confidence_basis_points: u16,
162    pub status: String,
163    pub source_scope: String,
164    pub source_path: String,
165    pub span: EvidenceSpan,
166    pub fingerprint: String,
167    pub freshness_cursor: String,
168}
169
170/// Bounded file-content query.
171#[derive(Debug, Clone, PartialEq, Eq)]
172pub struct FileContentSearchRequest {
173    pub query: String,
174    pub source_scope: Option<String>,
175    pub root_id: Option<String>,
176    pub authorized_roots: Vec<FileIndexRoot>,
177    pub limit: usize,
178    pub timeout_ms: u64,
179}
180
181/// One file-content search hit with provenance and prompt-role isolation.
182#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct FileContentSearchHit {
184    pub scope_id: String,
185    pub root_id: String,
186    pub path: String,
187    pub relative_path: String,
188    pub chunk_id: String,
189    pub content_role: String,
190    pub excerpt: String,
191    pub span: EvidenceSpan,
192    pub fingerprint: String,
193    pub content_hash: String,
194    pub indexed_at_ms: u64,
195    pub graph_version: u64,
196    pub indexed_graph_version: u64,
197    pub freshness_cursor: String,
198    pub rank: usize,
199    pub score: f64,
200    pub ranking_signals: Vec<String>,
201    pub fact_candidates: Vec<FileKnowledgeFactCandidate>,
202}
203
204/// Aggregated file-index diagnostics.
205#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
206pub struct FileIndexDiagnostics {
207    pub root_count: usize,
208    pub indexed_file_count: usize,
209    pub missing_file_count: usize,
210    pub indexed_content_count: usize,
211    pub skipped_content_count: usize,
212    pub unchanged_content_count: usize,
213    pub stale_content_cursor_count: usize,
214    pub scan_error_count: usize,
215    pub content_read_error_count: usize,
216    pub truncated_root_count: usize,
217    pub roots: Vec<FileIndexRootStatus>,
218    pub content_cursors: Vec<FileContentReadModelCursor>,
219}