Skip to main content

relay_knowledge/storage/
code.rs

1//! Storage contracts for code repository indexes.
2
3use crate::domain::{
4    CodeFileFingerprint, CodeImpactRequest, CodeIndexBatch, CodeIndexCheckpoint, CodeIndexSession,
5    CodeIndexSnapshot, CodeIndexSummary, CodeIndexTaskRecord, CodeRepositoryCrossEdge,
6    CodeRepositoryRegistration, CodeRepositoryReport, CodeRepositorySet, CodeRepositorySetMember,
7    CodeRepositorySetRefreshSummary, CodeRepositorySetRefreshTaskRecord, CodeRepositorySetStatus,
8    CodeRepositoryStatus, CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest,
9    CodeScopeRetentionSummary,
10};
11
12use super::{StorageError, StorageFuture};
13
14/// Diff-derived inputs used to seed code impact expansion.
15#[derive(Debug, Clone, Default, PartialEq, Eq)]
16pub struct CodeImpactChanges {
17    pub paths: Vec<String>,
18    pub deleted_symbol_names: Vec<String>,
19}
20
21/// New background code index task to persist or deduplicate.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct CodeIndexTaskSeed {
24    pub repository_id: String,
25    pub alias: String,
26    pub ref_selector: String,
27    pub resolved_commit_sha: String,
28    pub tree_hash: String,
29    pub source_scope: String,
30    pub path_filters: Vec<String>,
31    pub language_filters: Vec<String>,
32    pub mode: crate::domain::CodeIndexMode,
33    pub input_fingerprint: String,
34    pub resource_budget: crate::domain::CodeIndexResourceBudget,
35    pub payload_json: String,
36    pub now_ms: u64,
37}
38
39/// Lease acquisition request for one background code index task.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct CodeIndexTaskClaimRequest {
42    pub task_id: Option<String>,
43    pub lease_owner: String,
44    pub lease_duration_ms: u64,
45    pub max_attempts: u32,
46    pub now_ms: u64,
47}
48
49/// Completion report guarded by task lease and attempt token.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct CodeIndexTaskCompletion {
52    pub task_id: String,
53    pub lease_owner: String,
54    pub attempt_count: u32,
55    pub now_ms: u64,
56}
57
58/// Failure report for retry and dead-letter handling.
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct CodeIndexTaskFailure {
61    pub task_id: String,
62    pub lease_owner: String,
63    pub attempt_count: u32,
64    pub error_kind: String,
65    pub error_message: String,
66    pub retry_backoff_ms: u64,
67    pub max_attempts: u32,
68    pub now_ms: u64,
69}
70
71/// Scope retention request after a repository index completes.
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct CodeScopeRetentionRequest {
74    pub repository_id: String,
75    pub active_scope: String,
76    pub retain_recent_successful_scopes: usize,
77}
78
79/// New repository set metadata to persist.
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct CodeRepositorySetSeed {
82    pub alias: String,
83    pub description: Option<String>,
84    pub default_ref_policy_json: String,
85    pub now_ms: u64,
86}
87
88/// New or replaced repository-set member pointer.
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct CodeRepositorySetMemberSeed {
91    pub set_alias: String,
92    pub repository_id: String,
93    pub repository_alias: String,
94    pub ref_selector: String,
95    pub resolved_commit_sha: String,
96    pub source_scope: String,
97    pub path_filters: Vec<String>,
98    pub language_filters: Vec<String>,
99    pub priority: i32,
100}
101
102/// Repository-set overlay refresh task to persist or deduplicate.
103#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct CodeRepositorySetRefreshTaskSeed {
105    pub set_id: String,
106    pub set_alias: String,
107    pub input_fingerprint: String,
108    pub now_ms: u64,
109}
110
111/// Lease acquisition request for one repository-set overlay task.
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct CodeRepositorySetRefreshTaskClaimRequest {
114    pub task_id: Option<String>,
115    pub lease_owner: String,
116    pub lease_duration_ms: u64,
117    pub max_attempts: u32,
118    pub now_ms: u64,
119}
120
121/// Completion report guarded by task lease and attempt token.
122#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct CodeRepositorySetRefreshTaskCompletion {
124    pub task_id: String,
125    pub lease_owner: String,
126    pub attempt_count: u32,
127    pub now_ms: u64,
128}
129
130/// Failure report for retry and dead-letter handling.
131#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct CodeRepositorySetRefreshTaskFailure {
133    pub task_id: String,
134    pub lease_owner: String,
135    pub attempt_count: u32,
136    pub error_kind: String,
137    pub error_message: String,
138    pub retry_backoff_ms: u64,
139    pub max_attempts: u32,
140    pub now_ms: u64,
141}
142
143/// Persisted code repository graph and retrieval contract.
144pub trait CodeRepositoryStore: Send + Sync {
145    fn upsert_code_repository(
146        &self,
147        registration: CodeRepositoryRegistration,
148    ) -> StorageFuture<'_, CodeRepositoryStatus>;
149
150    fn code_repository_status(
151        &self,
152        repository: String,
153    ) -> StorageFuture<'_, Option<CodeRepositoryStatus>>;
154
155    fn code_repository_scope_status(
156        &self,
157        repository: String,
158        resolved_commit_sha: String,
159        path_filters: Vec<String>,
160        language_filters: Vec<String>,
161    ) -> StorageFuture<'_, Option<CodeRepositoryStatus>>;
162
163    fn queue_code_index_task(
164        &self,
165        task: CodeIndexTaskSeed,
166    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
167
168    fn claim_code_index_task(
169        &self,
170        request: CodeIndexTaskClaimRequest,
171    ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
172
173    fn complete_code_index_task(
174        &self,
175        request: CodeIndexTaskCompletion,
176    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
177
178    fn fail_code_index_task(
179        &self,
180        request: CodeIndexTaskFailure,
181    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
182
183    fn code_index_task(&self, task_id: String) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
184
185    fn active_code_index_task(
186        &self,
187        repository_id: String,
188    ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
189
190    fn code_index_checkpoint(
191        &self,
192        source_scope: String,
193    ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>>;
194
195    fn code_scope_retention(
196        &self,
197        repository_id: String,
198    ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
199
200    fn prune_code_repository_scopes(
201        &self,
202        request: CodeScopeRetentionRequest,
203    ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
204
205    fn code_file_fingerprints(
206        &self,
207        repository_id: String,
208    ) -> StorageFuture<'_, Vec<CodeFileFingerprint>>;
209
210    fn code_file_fingerprints_for_scope(
211        &self,
212        source_scope: String,
213    ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
214        Box::pin(async move {
215            Err(StorageError::InvalidInput(format!(
216                "code file fingerprints for scope '{source_scope}' are unavailable"
217            )))
218        })
219    }
220
221    fn code_file_candidate_paths_for_scope(
222        &self,
223        source_scope: String,
224        _path_filters: Vec<String>,
225        _language_filters: Vec<String>,
226        _limit: usize,
227    ) -> StorageFuture<'_, Vec<String>> {
228        Box::pin(async move {
229            Err(StorageError::InvalidInput(format!(
230                "bounded code file candidate paths for scope '{source_scope}' are unavailable"
231            )))
232        })
233    }
234
235    fn apply_code_index_snapshot(
236        &self,
237        snapshot: CodeIndexSnapshot,
238    ) -> StorageFuture<'_, CodeIndexSummary>;
239
240    fn begin_code_index_session(
241        &self,
242        session: CodeIndexSession,
243    ) -> StorageFuture<'_, CodeIndexCheckpoint> {
244        Box::pin(async move {
245            Err(StorageError::InvalidInput(format!(
246                "checkpointed code index sessions for scope '{}' are unavailable",
247                session.source_scope
248            )))
249        })
250    }
251
252    fn apply_code_index_batch(
253        &self,
254        batch: CodeIndexBatch,
255    ) -> StorageFuture<'_, CodeIndexCheckpoint> {
256        Box::pin(async move {
257            Err(StorageError::InvalidInput(format!(
258                "checkpointed code index batches for scope '{}' are unavailable",
259                batch.source_scope
260            )))
261        })
262    }
263
264    fn finalize_code_index_session(
265        &self,
266        session: CodeIndexSession,
267    ) -> StorageFuture<'_, CodeIndexSummary> {
268        Box::pin(async move {
269            Err(StorageError::InvalidInput(format!(
270                "checkpointed code index finalization for scope '{}' is unavailable",
271                session.source_scope
272            )))
273        })
274    }
275
276    fn search_code(
277        &self,
278        request: CodeRetrievalRequest,
279    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
280
281    fn search_code_scope(
282        &self,
283        source_scope: String,
284        _request: CodeRetrievalRequest,
285    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
286        Box::pin(async move {
287            Err(StorageError::InvalidInput(format!(
288                "code search for source scope '{source_scope}' is unavailable"
289            )))
290        })
291    }
292
293    fn analyze_code_impact(
294        &self,
295        request: CodeImpactRequest,
296        changes: CodeImpactChanges,
297    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
298
299    fn code_repository_totals(&self) -> StorageFuture<'_, CodeRepositoryTotals> {
300        Box::pin(async { Ok(CodeRepositoryTotals::default()) })
301    }
302
303    fn code_repository_report(
304        &self,
305        repository: String,
306    ) -> StorageFuture<'_, CodeRepositoryReport> {
307        Box::pin(async move {
308            Err(StorageError::InvalidInput(format!(
309                "code repository report for '{repository}' is unavailable"
310            )))
311        })
312    }
313
314    fn create_code_repository_set(
315        &self,
316        _seed: CodeRepositorySetSeed,
317    ) -> StorageFuture<'_, CodeRepositorySet> {
318        Box::pin(async {
319            Err(StorageError::InvalidInput(
320                "repository set storage is unavailable".to_owned(),
321            ))
322        })
323    }
324
325    fn add_code_repository_set_member(
326        &self,
327        _seed: CodeRepositorySetMemberSeed,
328    ) -> StorageFuture<'_, CodeRepositorySetMember> {
329        Box::pin(async {
330            Err(StorageError::InvalidInput(
331                "repository set member storage is unavailable".to_owned(),
332            ))
333        })
334    }
335
336    fn remove_code_repository_set_member(
337        &self,
338        _set_alias: String,
339        _repository_alias: String,
340    ) -> StorageFuture<'_, CodeRepositorySetMember> {
341        Box::pin(async {
342            Err(StorageError::InvalidInput(
343                "repository set member storage is unavailable".to_owned(),
344            ))
345        })
346    }
347
348    fn code_repository_set(
349        &self,
350        _set_alias: String,
351    ) -> StorageFuture<'_, Option<CodeRepositorySet>> {
352        Box::pin(async { Ok(None) })
353    }
354
355    fn code_repository_set_status(
356        &self,
357        _set_alias: String,
358    ) -> StorageFuture<'_, Option<CodeRepositorySetStatus>> {
359        Box::pin(async { Ok(None) })
360    }
361
362    fn refresh_code_repository_set_overlay(
363        &self,
364        _set_alias: String,
365        _now_ms: u64,
366    ) -> StorageFuture<'_, CodeRepositorySetRefreshSummary> {
367        Box::pin(async {
368            Err(StorageError::InvalidInput(
369                "repository set overlay refresh is unavailable".to_owned(),
370            ))
371        })
372    }
373
374    fn code_repository_set_cross_edges(
375        &self,
376        _set_id: String,
377    ) -> StorageFuture<'_, Vec<CodeRepositoryCrossEdge>> {
378        Box::pin(async { Ok(Vec::new()) })
379    }
380
381    fn queue_code_repository_set_refresh_task(
382        &self,
383        _task: CodeRepositorySetRefreshTaskSeed,
384    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
385        Box::pin(async {
386            Err(StorageError::InvalidInput(
387                "repository set refresh task storage is unavailable".to_owned(),
388            ))
389        })
390    }
391
392    fn claim_code_repository_set_refresh_task(
393        &self,
394        _request: CodeRepositorySetRefreshTaskClaimRequest,
395    ) -> StorageFuture<'_, Option<CodeRepositorySetRefreshTaskRecord>> {
396        Box::pin(async { Ok(None) })
397    }
398
399    fn complete_code_repository_set_refresh_task(
400        &self,
401        _request: CodeRepositorySetRefreshTaskCompletion,
402    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
403        Box::pin(async {
404            Err(StorageError::InvalidInput(
405                "repository set refresh task storage is unavailable".to_owned(),
406            ))
407        })
408    }
409
410    fn fail_code_repository_set_refresh_task(
411        &self,
412        _request: CodeRepositorySetRefreshTaskFailure,
413    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
414        Box::pin(async {
415            Err(StorageError::InvalidInput(
416                "repository set refresh task storage is unavailable".to_owned(),
417            ))
418        })
419    }
420}