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