1pub mod commands;
2pub mod config;
3pub mod contract;
4pub mod db;
5pub mod freshness;
6pub mod git;
7pub mod graph;
8pub mod index;
9pub(crate) mod index_lock;
10pub mod models;
11pub mod output;
12pub mod progress;
13pub mod project;
14pub mod projection;
15pub mod savings;
16pub mod schema;
17pub mod search;
18pub mod secrets;
19pub mod setup;
20pub mod skill;
21#[doc(hidden)]
22pub mod test_env;
23pub mod utils;
24pub mod vector;
25pub(crate) mod visibility;
26
27pub use index::api::{
28 IndexDegradation, IndexDurations, IndexOutcome, IndexRequest, UnsupportedFileType, index_files,
29};
30
31#[cfg(test)]
32mod tests {
33 use serde::Serialize;
34 use serde::de::DeserializeOwned;
35
36 fn assert_cli_independent_contract<T>()
37 where
38 T: Serialize + DeserializeOwned,
39 {
40 let type_name = std::any::type_name::<T>();
41 assert!(!type_name.contains("commands::"), "{type_name}");
42 assert!(!type_name.contains("output::"), "{type_name}");
43 assert!(!type_name.contains("clap"), "{type_name}");
44 }
45
46 #[test]
47 fn public_projection_api_is_cli_independent() {
48 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
49 for rel_path in [
50 "src/index/api.rs",
51 "src/graph/typed_query.rs",
52 "src/graph/code_graph.rs",
53 "src/vector/code_symbols.rs",
54 "src/projection/sync.rs",
55 ] {
56 assert!(
57 manifest_dir.join(rel_path).exists(),
58 "missing projection boundary module {rel_path}"
59 );
60 }
61
62 assert_cli_independent_contract::<crate::index::api::CodeFactWriteRequest>();
63 assert_cli_independent_contract::<crate::index::api::CodeFactWriteSummary>();
64 assert_cli_independent_contract::<crate::index::api::IndexRequest>();
65 assert_cli_independent_contract::<crate::index::api::IndexOutcome>();
66 assert_cli_independent_contract::<crate::index::api::IndexDurations>();
67 assert_cli_independent_contract::<crate::index::api::IndexDegradation>();
68 assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleRequest>();
69 assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleOutput>();
70 assert_cli_independent_contract::<crate::graph::code_graph::GraphReadRequest>();
71 assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchRequest>(
72 );
73 assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchHit>();
74 assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorPayload>();
75 assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncRequest>();
76 assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncStatus>();
77 }
78
79 #[test]
80 fn foundation_consumer_migration() {
81 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
82 let cargo = std::fs::read_to_string(manifest_dir.join("Cargo.toml"))
83 .expect("read gobby-code Cargo.toml");
84 for feature in ["postgres", "falkor", "qdrant", "search", "indexing"] {
85 assert!(
86 cargo.contains(feature),
87 "gobby-code must enable gobby-core feature `{feature}`"
88 );
89 }
90
91 let config_root =
92 std::fs::read_to_string(manifest_dir.join("src/config.rs")).expect("read config.rs");
93 let config_services = std::fs::read_to_string(manifest_dir.join("src/config/services.rs"))
94 .expect("read config/services.rs");
95 assert!(config_services.contains("gobby_core::config::{"));
96 assert!(config_services.contains("ConfigSource"));
97 assert!(config_services.contains("StandaloneConfig"));
98 assert!(config_services.contains("impl ServiceConfigSource for PostgresConfigSource"));
99 assert!(config_services.contains("struct ErrorCapturingConfigSource"));
100 assert!(config_services.contains("impl<S> ConfigSource for ErrorCapturingConfigSource"));
101 assert!(!config_services.contains("impl ConfigSource for PostgresConfigSource"));
102 assert!(config_services.contains("gobby_core::postgres::read_config_value"));
103 assert!(config_services.contains("gobby_core::config::decode_config_value"));
104 assert!(!config_root.contains("fn decode_config_value("));
105
106 let db = [
107 std::fs::read_to_string(manifest_dir.join("src/db/mod.rs")).expect("read db/mod.rs"),
108 std::fs::read_to_string(manifest_dir.join("src/db/resolution.rs"))
109 .expect("read db/resolution.rs"),
110 ]
111 .join("\n");
112 assert!(db.contains("gobby_core::gobby_home"));
113 assert!(db.contains("gobby_core::postgres::connect_readonly"));
114 assert!(db.contains("gobby_core::postgres::connect_readwrite"));
115 assert!(!db.contains("Client::connect(database_url, NoTls)"));
116
117 let graph = [
118 std::fs::read_to_string(manifest_dir.join("src/graph/code_graph.rs"))
119 .expect("read graph/code_graph.rs"),
120 std::fs::read_to_string(manifest_dir.join("src/graph/code_graph/connection.rs"))
121 .expect("read graph/code_graph/connection.rs"),
122 std::fs::read_to_string(manifest_dir.join("src/graph/code_graph/write.rs"))
123 .expect("read graph/code_graph/write.rs"),
124 ]
125 .join("\n");
126 assert!(graph.contains("gobby_core::falkor::with_graph"));
127 assert!(!graph.contains("falkor::with_falkor"));
128
129 assert!(
130 !manifest_dir.join("src/falkor.rs").exists(),
131 "graph callers should use graph::code_graph or gobby_core::falkor directly"
132 );
133 assert!(
134 !manifest_dir.join("src/search/semantic.rs").exists(),
135 "semantic search callers should use vector::code_symbols directly"
136 );
137
138 let code_symbols_qdrant =
139 std::fs::read_to_string(manifest_dir.join("src/vector/code_symbols/qdrant.rs"))
140 .expect("read vector/code_symbols/qdrant.rs");
141 assert!(code_symbols_qdrant.contains("gobby_core::qdrant::with_qdrant"));
142 assert!(code_symbols_qdrant.contains("gobby_core::qdrant::collection_name"));
143 assert!(code_symbols_qdrant.contains("gobby_core::qdrant::search"));
144 }
145
146 #[test]
147 fn indexing_search_primitive_migration() {
148 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
149
150 let walker = [
151 std::fs::read_to_string(manifest_dir.join("src/index/walker.rs"))
152 .expect("read index/walker.rs"),
153 std::fs::read_to_string(manifest_dir.join("src/index/walker/classification.rs"))
154 .expect("read index/walker/classification.rs"),
155 std::fs::read_to_string(manifest_dir.join("src/index/walker/discovery.rs"))
156 .expect("read index/walker/discovery.rs"),
157 ]
158 .join("\n");
159 assert!(walker.contains("gobby_core::indexing::WalkerSettings"));
160 let local_walker_builder = ["WalkBuilder", "::new(root)"].concat();
161 assert!(!walker.contains(&local_walker_builder));
162
163 let hasher = std::fs::read_to_string(manifest_dir.join("src/index/hasher.rs"))
164 .expect("read index/hasher.rs");
165 assert!(hasher.contains("gobby_core::indexing::file_content_hash"));
166 let local_buffer = format!("let mut buf = [0u8; {}]", 64 * 1024);
167 assert!(!hasher.contains(&local_buffer));
168
169 let rrf =
170 std::fs::read_to_string(manifest_dir.join("src/search/rrf.rs")).expect("read rrf.rs");
171 assert!(rrf.contains("gobby_core::search::rrf_merge"));
172 let local_rrf_const = ["const ", "RRF_K"].concat();
173 assert!(!rrf.contains(&local_rrf_const));
174
175 let chunker = std::fs::read_to_string(manifest_dir.join("src/index/chunker.rs"))
176 .expect("read index/chunker.rs");
177 assert!(!chunker.contains("use gobby_core::indexing::Chunk"));
178 assert!(!chunker.contains("use gobby_core::indexing::ChunkIdentity"));
179 assert!(!chunker.contains("use gobby_core::indexing::IndexEvent"));
180 assert!(!chunker.contains("use gobby_core::indexing::index_events_from_hashes"));
181 }
182
183 #[test]
184 fn falkor_facade_uses_core_graph_client_only() {
185 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
186 let src_dir = manifest_dir.join("src");
187 let mut offenders = Vec::new();
188
189 fn visit(path: &std::path::Path, offenders: &mut Vec<std::path::PathBuf>) {
190 for entry in std::fs::read_dir(path).expect("read source directory") {
191 let entry = entry.expect("source entry");
192 let path = entry.path();
193 if path.is_dir() {
194 visit(&path, offenders);
195 continue;
196 }
197 if path.extension().and_then(|ext| ext.to_str()) != Some("rs") {
198 continue;
199 }
200 let source = std::fs::read_to_string(&path).expect("read source file");
201 let builder = ["Falkor", "Client", "Builder"].concat();
202 if source.contains(&builder) {
203 offenders.push(path);
204 }
205 }
206 }
207
208 visit(&src_dir, &mut offenders);
209 assert!(
210 offenders.is_empty(),
211 "gobby-code must use gobby_core::falkor::GraphClient instead of direct FalkorDB builders: {offenders:?}"
212 );
213 }
214}