Skip to main content

relay_knowledge/storage/
code.rs

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