fix logfire span hierarchy with .entered()

- use .entered() on all spans to create proper parent-child relationships
- now all spans share same trace_id and properly nest under bufo_search parent
- before: all spans had parent_span_id: null (disconnected)
- after: child spans have parent_span_id pointing to bufo_search

verified locally with logfire MCP:
bufo_search (parent)
├─ voyage.embed_text
└─ turbopuffer.query

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+18 -16
src
+18 -16
src/search.rs
··· 55 55 query: web::Json<SearchQuery>, 56 56 config: web::Data<Config>, 57 57 ) -> ActixResult<HttpResponse> { 58 - let query_text = &query.query; 58 + let query_text = query.query.clone(); 59 59 let top_k_val = query.top_k; 60 60 61 61 let _search_span = logfire::span!( 62 62 "bufo_search", 63 - query = query_text, 63 + query = &query_text, 64 64 top_k = top_k_val as i64 65 - ); 65 + ).entered(); 66 66 67 67 logfire::info!( 68 68 "search request received", 69 - query = query_text, 69 + query = &query_text, 70 70 top_k = top_k_val as i64 71 71 ); 72 72 ··· 80 80 let query_embedding = { 81 81 let _span = logfire::span!( 82 82 "voyage.embed_text", 83 - query = query_text, 83 + query = &query_text, 84 84 model = "voyage-3-lite" 85 - ); 85 + ).entered(); 86 + 86 87 embedding_client 87 - .embed_text(query_text) 88 + .embed_text(&query_text) 88 89 .await 89 90 .map_err(|e| { 90 91 let error_msg = e.to_string(); 91 92 logfire::error!( 92 93 "embedding generation failed", 93 94 error = error_msg, 94 - query = query_text 95 + query = &query_text 95 96 ); 96 97 actix_web::error::ErrorInternalServerError(format!( 97 98 "failed to generate embedding: {}", ··· 102 103 103 104 logfire::info!( 104 105 "embedding generated", 105 - query = query_text, 106 + query = &query_text, 106 107 embedding_dim = query_embedding.len() as i64 107 108 ); 108 109 ··· 116 117 include_attributes: Some(vec!["url".to_string(), "name".to_string(), "filename".to_string()]), 117 118 }; 118 119 119 - let namespace = &config.turbopuffer_namespace; 120 + let namespace = config.turbopuffer_namespace.clone(); 120 121 let vector_results = { 121 122 let _span = logfire::span!( 122 123 "turbopuffer.query", 123 - query = query_text, 124 + query = &query_text, 124 125 top_k = top_k_val as i64, 125 - namespace = namespace 126 - ); 126 + namespace = &namespace 127 + ).entered(); 128 + 127 129 tpuf_client.query(vector_request).await.map_err(|e| { 128 130 let error_msg = e.to_string(); 129 131 logfire::error!( 130 132 "vector search failed", 131 133 error = error_msg, 132 - query = query_text, 134 + query = &query_text, 133 135 top_k = top_k_val as i64 134 136 ); 135 137 actix_web::error::ErrorInternalServerError(format!( ··· 145 147 146 148 logfire::info!( 147 149 "vector search completed", 148 - query = query_text, 150 + query = &query_text, 149 151 results_found = results_found, 150 152 min_dist = min_dist, 151 153 max_dist = max_dist ··· 194 196 195 197 logfire::info!( 196 198 "search completed successfully", 197 - query = query_text, 199 + query = &query_text, 198 200 results_count = results_count, 199 201 top_result = &top_result_name, 200 202 top_score = top_score_val,