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 latest_code_repository_scope_status(
182        &self,
183        _repository: String,
184        _path_filters: Vec<String>,
185        _language_filters: Vec<String>,
186    ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
187        Box::pin(async { Ok(None) })
188    }
189
190    fn queue_code_index_task(
191        &self,
192        task: CodeIndexTaskSeed,
193    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
194
195    fn claim_code_index_task(
196        &self,
197        request: CodeIndexTaskClaimRequest,
198    ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
199
200    fn recover_code_index_task_leases(
201        &self,
202        _now_ms: u64,
203        _max_attempts: u32,
204    ) -> StorageFuture<'_, ()> {
205        Box::pin(async {
206            Err(StorageError::InvalidInput(
207                CODE_INDEX_TASK_LEASE_RECOVERY_UNAVAILABLE.to_owned(),
208            ))
209        })
210    }
211
212    fn renew_code_index_task_lease(
213        &self,
214        _request: CodeIndexTaskLeaseRenewal,
215    ) -> StorageFuture<'_, CodeIndexTaskRecord> {
216        Box::pin(async {
217            Err(StorageError::InvalidInput(
218                CODE_INDEX_TASK_LEASE_RENEWAL_UNAVAILABLE.to_owned(),
219            ))
220        })
221    }
222
223    fn complete_code_index_task(
224        &self,
225        request: CodeIndexTaskCompletion,
226    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
227
228    fn fail_code_index_task(
229        &self,
230        request: CodeIndexTaskFailure,
231    ) -> StorageFuture<'_, CodeIndexTaskRecord>;
232
233    fn code_index_task(&self, task_id: String) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
234
235    fn active_code_index_task(
236        &self,
237        repository_id: String,
238    ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
239
240    fn code_index_checkpoint(
241        &self,
242        source_scope: String,
243    ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>>;
244
245    fn code_scope_retention(
246        &self,
247        repository_id: String,
248    ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
249
250    fn prune_code_repository_scopes(
251        &self,
252        request: CodeScopeRetentionRequest,
253    ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
254
255    fn code_file_fingerprints(
256        &self,
257        repository_id: String,
258    ) -> StorageFuture<'_, Vec<CodeFileFingerprint>>;
259
260    fn code_file_fingerprints_for_scope(
261        &self,
262        source_scope: String,
263    ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
264        Box::pin(async move {
265            Err(StorageError::InvalidInput(format!(
266                "code file fingerprints for scope '{source_scope}' are unavailable"
267            )))
268        })
269    }
270
271    fn code_file_candidate_paths_for_scope(
272        &self,
273        source_scope: String,
274        _path_filters: Vec<String>,
275        _language_filters: Vec<String>,
276        _limit: usize,
277    ) -> StorageFuture<'_, Vec<String>> {
278        Box::pin(async move {
279            Err(StorageError::InvalidInput(format!(
280                "bounded code file candidate paths for scope '{source_scope}' are unavailable"
281            )))
282        })
283    }
284
285    fn code_file_candidate_paths_for_query_scope(
286        &self,
287        source_scope: String,
288        _query: String,
289        path_filters: Vec<String>,
290        language_filters: Vec<String>,
291        limit: usize,
292    ) -> StorageFuture<'_, Vec<String>> {
293        self.code_file_candidate_paths_for_scope(
294            source_scope,
295            path_filters,
296            language_filters,
297            limit,
298        )
299    }
300
301    fn apply_code_index_snapshot(
302        &self,
303        snapshot: CodeIndexSnapshot,
304    ) -> StorageFuture<'_, CodeIndexSummary>;
305
306    fn begin_code_index_session(
307        &self,
308        session: CodeIndexSession,
309    ) -> StorageFuture<'_, CodeIndexCheckpoint> {
310        Box::pin(async move {
311            Err(StorageError::InvalidInput(format!(
312                "checkpointed code index sessions for scope '{}' are unavailable",
313                session.source_scope
314            )))
315        })
316    }
317
318    fn apply_code_index_batch(
319        &self,
320        batch: CodeIndexBatch,
321    ) -> StorageFuture<'_, CodeIndexCheckpoint> {
322        Box::pin(async move {
323            Err(StorageError::InvalidInput(format!(
324                "checkpointed code index batches for scope '{}' are unavailable",
325                batch.source_scope
326            )))
327        })
328    }
329
330    fn finalize_code_index_session(
331        &self,
332        session: CodeIndexSession,
333    ) -> StorageFuture<'_, CodeIndexSummary> {
334        Box::pin(async move {
335            Err(StorageError::InvalidInput(format!(
336                "checkpointed code index finalization for scope '{}' is unavailable",
337                session.source_scope
338            )))
339        })
340    }
341
342    fn search_code(
343        &self,
344        request: CodeRetrievalRequest,
345    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
346
347    fn search_code_feature_flags(
348        &self,
349        request: CodeFeatureFlagRequest,
350    ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
351        Box::pin(async move {
352            Err(StorageError::InvalidInput(format!(
353                "code feature flag search for repository '{}' is unavailable",
354                request.repository.repository
355            )))
356        })
357    }
358
359    fn search_code_feature_flags_scope(
360        &self,
361        source_scope: String,
362        _request: CodeFeatureFlagRequest,
363    ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
364        Box::pin(async move {
365            Err(StorageError::InvalidInput(format!(
366                "code feature flag search for source scope '{source_scope}' is unavailable"
367            )))
368        })
369    }
370
371    fn search_code_scope(
372        &self,
373        source_scope: String,
374        _request: CodeRetrievalRequest,
375    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
376        Box::pin(async move {
377            Err(StorageError::InvalidInput(format!(
378                "code search for source scope '{source_scope}' is unavailable"
379            )))
380        })
381    }
382
383    fn analyze_code_impact(
384        &self,
385        request: CodeImpactRequest,
386        changes: CodeImpactChanges,
387    ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
388
389    fn code_repository_totals(&self) -> StorageFuture<'_, CodeRepositoryTotals> {
390        Box::pin(async { Ok(CodeRepositoryTotals::default()) })
391    }
392
393    fn code_repository_report(
394        &self,
395        repository: String,
396    ) -> StorageFuture<'_, CodeRepositoryReport> {
397        Box::pin(async move {
398            Err(StorageError::InvalidInput(format!(
399                "code repository report for '{repository}' is unavailable"
400            )))
401        })
402    }
403
404    fn create_code_repository_set(
405        &self,
406        _seed: CodeRepositorySetSeed,
407    ) -> StorageFuture<'_, CodeRepositorySet> {
408        Box::pin(async {
409            Err(StorageError::InvalidInput(
410                "repository set storage is unavailable".to_owned(),
411            ))
412        })
413    }
414
415    fn add_code_repository_set_member(
416        &self,
417        _seed: CodeRepositorySetMemberSeed,
418    ) -> StorageFuture<'_, CodeRepositorySetMember> {
419        Box::pin(async {
420            Err(StorageError::InvalidInput(
421                "repository set member storage is unavailable".to_owned(),
422            ))
423        })
424    }
425
426    fn remove_code_repository_set_member(
427        &self,
428        _set_alias: String,
429        _repository_alias: String,
430    ) -> StorageFuture<'_, CodeRepositorySetMember> {
431        Box::pin(async {
432            Err(StorageError::InvalidInput(
433                "repository set member storage is unavailable".to_owned(),
434            ))
435        })
436    }
437
438    fn code_repository_set(
439        &self,
440        _set_alias: String,
441    ) -> StorageFuture<'_, Option<CodeRepositorySet>> {
442        Box::pin(async { Ok(None) })
443    }
444
445    fn code_repository_set_status(
446        &self,
447        _set_alias: String,
448    ) -> StorageFuture<'_, Option<CodeRepositorySetStatus>> {
449        Box::pin(async { Ok(None) })
450    }
451
452    fn refresh_code_repository_set_overlay(
453        &self,
454        _set_alias: String,
455        _now_ms: u64,
456    ) -> StorageFuture<'_, CodeRepositorySetRefreshSummary> {
457        Box::pin(async {
458            Err(StorageError::InvalidInput(
459                "repository set overlay refresh is unavailable".to_owned(),
460            ))
461        })
462    }
463
464    fn code_repository_set_cross_edges(
465        &self,
466        _set_id: String,
467    ) -> StorageFuture<'_, Vec<CodeRepositoryCrossEdge>> {
468        Box::pin(async { Ok(Vec::new()) })
469    }
470
471    fn queue_code_repository_set_refresh_task(
472        &self,
473        _task: CodeRepositorySetRefreshTaskSeed,
474    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
475        Box::pin(async {
476            Err(StorageError::InvalidInput(
477                "repository set refresh task storage is unavailable".to_owned(),
478            ))
479        })
480    }
481
482    fn claim_code_repository_set_refresh_task(
483        &self,
484        _request: CodeRepositorySetRefreshTaskClaimRequest,
485    ) -> StorageFuture<'_, Option<CodeRepositorySetRefreshTaskRecord>> {
486        Box::pin(async { Ok(None) })
487    }
488
489    fn complete_code_repository_set_refresh_task(
490        &self,
491        _request: CodeRepositorySetRefreshTaskCompletion,
492    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
493        Box::pin(async {
494            Err(StorageError::InvalidInput(
495                "repository set refresh task storage is unavailable".to_owned(),
496            ))
497        })
498    }
499
500    fn fail_code_repository_set_refresh_task(
501        &self,
502        _request: CodeRepositorySetRefreshTaskFailure,
503    ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
504        Box::pin(async {
505            Err(StorageError::InvalidInput(
506                "repository set refresh task storage is unavailable".to_owned(),
507            ))
508        })
509    }
510}