Skip to main content

fallow_output/
similar_code.rs

1//! Independent JSON contracts for opt-in semantic similar-code discovery.
2
3use std::fmt;
4
5use crate::root_envelopes::{RootEnvelopeMode, serialize_named_json_output};
6use fallow_types::envelope::{ElapsedMs, ToolVersion};
7use serde::{Deserialize, Serialize};
8
9/// Current raw similar-code envelope schema version.
10pub const SIMILAR_CODE_SCHEMA_VERSION: u32 = 1;
11/// Current similar-code inspect envelope schema version.
12pub const SIMILAR_CODE_INSPECT_SCHEMA_VERSION: u32 = 1;
13/// Current similar-code review envelope schema version.
14pub const SIMILAR_CODE_REVIEW_SCHEMA_VERSION: u32 = 1;
15/// Current local-provider status envelope schema version.
16pub const SIMILAR_CODE_STATUS_SCHEMA_VERSION: u32 = 1;
17/// Current vector-cache clear envelope schema version.
18pub const SIMILAR_CODE_CACHE_CLEAR_SCHEMA_VERSION: u32 = 1;
19
20/// Version singleton for raw similar-code output.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
22#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
23pub enum SimilarCodeSchemaVersion {
24    /// Initial independent contract.
25    #[serde(rename = "1")]
26    V1,
27}
28
29/// Version singleton for a similar-code inspect packet.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
31#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
32pub enum SimilarCodeInspectSchemaVersion {
33    /// Initial independent contract.
34    #[serde(rename = "1")]
35    V1,
36}
37
38/// Version singleton for reviewed similar-code output.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
40#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
41pub enum SimilarCodeReviewSchemaVersion {
42    /// Initial independent contract.
43    #[serde(rename = "1")]
44    V1,
45}
46
47/// Version singleton for local-provider status output.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
49#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
50pub enum SimilarCodeStatusSchemaVersion {
51    /// Initial independent status contract.
52    #[serde(rename = "1")]
53    V1,
54}
55
56/// Version singleton for vector-cache clear output.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
58#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
59pub enum SimilarCodeCacheClearSchemaVersion {
60    /// Initial independent cache-mutation contract.
61    #[serde(rename = "1")]
62    V1,
63}
64
65/// Machine-readable readiness of the exact local companion and pinned model.
66#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
67#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
68pub struct SimilarCodeStatusOutput {
69    /// Independent status schema version.
70    pub schema_version: SimilarCodeStatusSchemaVersion,
71    /// Fallow version producing the status envelope.
72    pub version: ToolVersion,
73    /// Companion protocol version.
74    pub protocol_version: u32,
75    /// Embedding calculation semantics implemented by the companion.
76    pub embedding_semantics_version: u32,
77    /// Exact companion package version.
78    pub companion_version: String,
79    /// Whether every pinned model artifact is ready and verified.
80    pub model_ready: bool,
81    /// Immutable model identifier.
82    pub model_id: String,
83    /// Immutable model revision.
84    pub model_revision: String,
85    /// Embedding width.
86    pub dimensions: u32,
87    /// Maximum tokenizer length.
88    pub max_tokens: u32,
89    /// Model license identifier.
90    pub license: String,
91    /// Local model cache directory.
92    pub cache_dir: String,
93    /// Expected download size for all pinned artifacts.
94    pub download_bytes: u64,
95    /// Whether source analysis stays local and offline.
96    pub analysis_offline: bool,
97    /// Whether all installed artifacts passed integrity validation.
98    pub integrity_verified: bool,
99    /// Actionable readiness problem when the model is unavailable.
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub problem: Option<String>,
102    /// Whether this setup invocation downloaded new bytes.
103    #[serde(default, skip_serializing_if = "Option::is_none")]
104    pub downloaded: Option<bool>,
105}
106
107/// Result of explicitly clearing the derived project-namespaced vector cache.
108#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
109#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
110pub struct SimilarCodeCacheClearOutput {
111    /// Independent cache-clear schema version.
112    pub schema_version: SimilarCodeCacheClearSchemaVersion,
113    /// Fallow version producing the cache-clear envelope.
114    pub version: ToolVersion,
115    /// Whether an existing vector cache was removed.
116    pub removed: bool,
117    /// Whether model artifacts were removed. Version 1 always emits false.
118    pub model_removed: bool,
119}
120
121/// Version singleton for the separate human or agent verdict document.
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
123#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
124pub enum SimilarCodeVerdictSchemaVersion {
125    /// Initial independent verdict contract.
126    #[serde(rename = "1")]
127    V1,
128}
129
130/// Immutable local provider provenance for one generation run.
131#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
132#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
133pub struct SimilarCodeProviderProvenance {
134    /// Provider family. Version 1 accepts only the official local companion.
135    pub provider: SimilarCodeProvider,
136    /// Exact companion package version.
137    pub companion_version: String,
138    /// Companion protocol version negotiated for this run.
139    pub protocol_version: u32,
140    /// Whether source content left the local machine. Version 1 requires false.
141    pub source_left_machine: bool,
142}
143
144/// Provider families admitted by the version 1 public contract.
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
146#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
147#[serde(rename_all = "kebab-case")]
148pub enum SimilarCodeProvider {
149    /// Exact-version official companion executed locally.
150    OfficialLocalCompanion,
151}
152
153/// Immutable model artifact provenance.
154#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
155#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
156pub struct SimilarCodeModelProvenance {
157    /// Stable model identifier.
158    pub model_id: String,
159    /// Immutable model revision.
160    pub revision: String,
161    /// SHA-256 digest of the exact model artifact bytes.
162    pub artifact_sha256: String,
163    /// SPDX license identifier or reviewed license label.
164    pub license: String,
165    /// Embedding vector dimensions.
166    pub dimensions: u32,
167}
168
169/// Parameters that materially affect generated embeddings and scores.
170#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
171#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
172pub struct SimilarCodeGenerationParameters {
173    /// Numeric representation used for model inference.
174    pub dtype: String,
175    /// Pooling strategy applied to model output.
176    pub pooling: String,
177    /// Whether vectors were normalized before comparison.
178    pub normalized: bool,
179    /// Maximum inference batch size used by the run.
180    pub batch_size: u32,
181    /// Maximum tokenizer length before deterministic truncation.
182    pub max_tokens: u32,
183    /// Digest over the complete effective generation parameter set.
184    pub parameter_sha256: String,
185}
186
187/// Effective endpoint scope used for corpus admission and pair retention.
188#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
189#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
190pub struct SimilarCodeScopeProvenance {
191    /// Whether file, changed-file, diff, or workspace scoping was active.
192    pub active: bool,
193    /// Sorted project-root-relative paths satisfying every active predicate.
194    pub paths: Vec<String>,
195}
196
197/// Complete provenance needed to reproduce candidate generation.
198#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
199#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
200pub struct SimilarCodeGeneration {
201    /// Version of extraction and normalization semantics used for both IDs.
202    pub extraction_semantics_version: u32,
203    /// Version of the calculation that produces model embeddings.
204    pub embedding_semantics_version: u32,
205    /// Local provider provenance.
206    pub provider: SimilarCodeProviderProvenance,
207    /// Immutable model provenance.
208    pub model: SimilarCodeModelProvenance,
209    /// Effective generation parameters.
210    pub parameters: SimilarCodeGenerationParameters,
211    /// Materialized endpoint scope needed to reproduce scoped discovery.
212    pub scope: SimilarCodeScopeProvenance,
213    /// Minimum cosine similarity admitted into the candidate set.
214    pub threshold: f64,
215    /// Minimum source line count admitted into function extraction.
216    pub min_lines: u64,
217}
218
219/// Exact named location of one candidate function.
220#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
221#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
222pub struct SimilarCodeLocation {
223    /// Project-root-relative, forward-slash path.
224    pub path: String,
225    /// Extracted function or method name.
226    pub name: String,
227    /// One-based inclusive start line.
228    pub start_line: u32,
229    /// One-based inclusive start column.
230    pub start_column: u32,
231    /// One-based inclusive end line.
232    pub end_line: u32,
233    /// One-based inclusive end column.
234    pub end_column: u32,
235    /// SHA-256 digest of the exact extracted function source.
236    pub source_sha256: String,
237}
238
239/// Stable, coarse interpretation of a candidate score.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
241#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
242#[serde(rename_all = "kebab-case")]
243pub enum SimilarCodeSimilarityBand {
244    /// Candidate is close to the configured lower threshold.
245    Moderate,
246    /// Candidate has a strong semantic similarity score.
247    High,
248    /// Candidate has an exceptionally high semantic similarity score.
249    VeryHigh,
250}
251
252/// Verification state of a raw semantic candidate.
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
254#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
255#[serde(rename_all = "kebab-case")]
256pub enum SimilarCodeVerificationStatus {
257    /// Candidate generation is discovery only and has not verified behavior.
258    Unverified,
259}
260
261/// Explicit availability state for one optional enrichment source.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
263#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
264#[serde(rename_all = "kebab-case")]
265pub enum SimilarCodeEnrichmentState {
266    /// Evidence is included in the inspect packet.
267    Available,
268    /// Evidence was requested but could not be obtained.
269    Unavailable,
270    /// Evidence was not requested for this run.
271    NotRequested,
272}
273
274/// Availability of every supported source-grounded enrichment.
275#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
276#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
277pub struct SimilarCodeEnrichmentAvailability {
278    /// Import or workspace relationship evidence.
279    pub graph_relationship: SimilarCodeEnrichmentState,
280    /// Entry-point reachability evidence.
281    pub entry_point_reachability: SimilarCodeEnrichmentState,
282    /// Direct caller evidence.
283    pub callers: SimilarCodeEnrichmentState,
284    /// Direct callee evidence.
285    pub callees: SimilarCodeEnrichmentState,
286    /// Ownership evidence.
287    pub ownership: SimilarCodeEnrichmentState,
288    /// Churn evidence.
289    pub churn: SimilarCodeEnrichmentState,
290    /// Test relationship evidence.
291    pub tests: SimilarCodeEnrichmentState,
292    /// Deterministic clone coverage evidence.
293    pub deterministic_clone_coverage: SimilarCodeEnrichmentState,
294    /// Runtime evidence.
295    pub runtime: SimilarCodeEnrichmentState,
296}
297
298/// Read-only follow-up exposed for a candidate.
299#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
300#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
301pub struct SimilarCodeAction {
302    /// Stable action identifier.
303    pub action: SimilarCodeActionType,
304    /// Human-readable description of the read-only operation.
305    pub description: String,
306    /// Explicit mutation guarantee. Version 1 requires this to be true.
307    pub read_only: bool,
308}
309
310/// Read-only actions supported by the candidate workflow.
311#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
312#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
313#[serde(rename_all = "kebab-case")]
314pub enum SimilarCodeActionType {
315    /// Build a bounded source-grounded inspect packet.
316    Inspect,
317    /// Join this candidate with an external verdict document.
318    Review,
319}
320
321/// One unverified semantic similar-code candidate.
322#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
323#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
324pub struct SimilarCodeCandidate {
325    /// Snapshot-stable opaque candidate identity.
326    pub candidate_id: String,
327    /// Content-stable key used for safe line-movement rebinding.
328    pub review_key: String,
329    /// First location in deterministic pair order.
330    pub left: SimilarCodeLocation,
331    /// Second location in deterministic pair order.
332    pub right: SimilarCodeLocation,
333    /// Cosine similarity reported by the pinned provider and model.
334    pub similarity: f64,
335    /// Stable score band for consumers that do not need the raw score.
336    pub similarity_band: SimilarCodeSimilarityBand,
337    /// Raw candidates are always explicitly unverified.
338    pub verification_status: SimilarCodeVerificationStatus,
339    /// Availability of optional deterministic and runtime context.
340    pub enrichment: SimilarCodeEnrichmentAvailability,
341    /// Read-only inspect and review affordances.
342    pub actions: Vec<SimilarCodeAction>,
343}
344
345/// Bounded phase names in the similar-code pipeline.
346#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
347#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
348#[serde(rename_all = "kebab-case")]
349pub enum SimilarCodePhase {
350    /// Discover eligible source files.
351    Discovery,
352    /// Extract and normalize supported functions.
353    Extraction,
354    /// Load or populate the local vector cache.
355    Cache,
356    /// Generate embeddings with the official local companion.
357    Embedding,
358    /// Validate provider output before using it.
359    Validation,
360    /// Compare vectors and select bounded candidates.
361    Comparison,
362    /// Build optional deterministic context.
363    Enrichment,
364}
365
366/// Completion state for one generation phase.
367#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
368#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
369#[serde(rename_all = "kebab-case")]
370pub enum SimilarCodePhaseStatus {
371    /// Phase completed its admitted scope.
372    Complete,
373    /// Phase returned bounded partial results.
374    Partial,
375    /// Phase was intentionally not run.
376    Skipped,
377    /// Phase reached its configured timeout.
378    TimedOut,
379}
380
381/// Accounting for one bounded generation phase.
382#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
383#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
384pub struct SimilarCodePhaseCompletion {
385    /// Pipeline phase.
386    pub phase: SimilarCodePhase,
387    /// Whether this phase completed, skipped, or returned partial data.
388    pub status: SimilarCodePhaseStatus,
389    /// Number of admitted inputs processed by this phase.
390    pub processed: u64,
391    /// Total admitted inputs known to this phase, when available.
392    pub total: Option<u64>,
393    /// Stable explanation when the phase did not complete.
394    pub reason: Option<String>,
395}
396
397/// Effective resource limits for a similar-code run.
398#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
399#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
400pub struct SimilarCodeLimits {
401    /// Maximum source files admitted.
402    pub max_files: u64,
403    /// Maximum extracted functions admitted.
404    pub max_functions: u64,
405    /// Maximum aggregate normalized source bytes admitted.
406    pub max_source_bytes: u64,
407    /// Maximum normalized bytes admitted for one function.
408    pub max_function_bytes: u64,
409    /// Maximum embedding batch size.
410    pub max_batch_size: u64,
411    /// Maximum vector bytes retained for comparison.
412    pub max_vector_bytes: u64,
413    /// Maximum pair comparisons performed.
414    pub max_comparisons: u64,
415    /// Maximum candidates returned.
416    pub max_candidates: u64,
417    /// Maximum returned neighbors per function.
418    pub max_neighbors_per_function: u64,
419    /// End-to-end timeout in milliseconds.
420    pub timeout_ms: u64,
421}
422
423/// Stable reasons why admitted work was skipped or truncated.
424#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
425#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
426#[serde(rename_all = "kebab-case")]
427pub enum SimilarCodeSkipReason {
428    /// Function was shorter than the configured minimum source-line count.
429    BelowMinimumLines,
430    /// Syntax form is outside the supported extraction contract.
431    UnsupportedFunction,
432    /// Generated source was excluded.
433    GeneratedSource,
434    /// One function exceeded the per-function byte limit.
435    FunctionTooLarge,
436    /// File or function admission limit was reached.
437    InputLimit,
438    /// Aggregate source byte limit was reached.
439    SourceBytesLimit,
440    /// Vector memory limit was reached.
441    VectorMemoryLimit,
442    /// Pair comparison limit was reached.
443    ComparisonLimit,
444    /// Candidate result limit was reached.
445    CandidateLimit,
446    /// Per-function neighbor limit was reached.
447    NeighborLimit,
448    /// Provider or overall timeout was reached.
449    Timeout,
450    /// Local provider failed after returning a usable subset of vectors.
451    ProviderFailure,
452    /// Provider tokenization truncated an otherwise admitted source fragment.
453    TokenTruncation,
454    /// Optional enrichment source was unavailable.
455    EnrichmentUnavailable,
456}
457
458/// Count of skipped work for a stable reason.
459#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
460#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
461pub struct SimilarCodeSkip {
462    /// Phase that skipped the work.
463    pub phase: SimilarCodePhase,
464    /// Stable skip reason.
465    pub reason: SimilarCodeSkipReason,
466    /// Number of inputs skipped for this phase and reason.
467    pub count: u64,
468}
469
470/// Vector cache outcome for a run.
471#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
472#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
473#[serde(rename_all = "kebab-case")]
474pub enum SimilarCodeCacheStatus {
475    /// Cache use was disabled.
476    Disabled,
477    /// Every requested vector was found in the cache.
478    Hit,
479    /// No requested vector was found in the cache.
480    Miss,
481    /// The run used both cached and newly generated vectors.
482    Mixed,
483}
484
485/// Privacy-safe cache accounting. Source fragments are never represented.
486#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
487#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
488pub struct SimilarCodeCacheSummary {
489    /// Aggregate cache outcome.
490    pub status: SimilarCodeCacheStatus,
491    /// Valid vector cache hits.
492    pub hits: u64,
493    /// Vector cache misses.
494    pub misses: u64,
495    /// Newly written vector cache entries.
496    pub writes: u64,
497    /// Corrupt or incompatible entries ignored safely.
498    pub invalid_entries: u64,
499}
500
501/// Overall trustworthiness of an emitted result set.
502#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
503#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
504#[serde(rename_all = "kebab-case")]
505pub enum SimilarCodeCompletionStatus {
506    /// Every admitted generation phase completed.
507    Complete,
508    /// One or more limits, skips, or timeouts made the result partial.
509    Partial,
510}
511
512/// Typed completion, limit, skip, and cache accounting.
513#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
514#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
515pub struct SimilarCodeCompletion {
516    /// Overall completion status. Only `complete` makes an empty set conclusive.
517    pub status: SimilarCodeCompletionStatus,
518    /// Per-phase completion in pipeline order.
519    pub phases: Vec<SimilarCodePhaseCompletion>,
520    /// Effective run limits.
521    pub limits: SimilarCodeLimits,
522    /// Aggregated skips in phase and reason order.
523    pub skips: Vec<SimilarCodeSkip>,
524    /// Privacy-safe vector cache accounting.
525    pub cache: SimilarCodeCacheSummary,
526    /// Aggregate model inference wall time reported by the local provider.
527    pub provider_inference_ms: u64,
528}
529
530/// Non-severity diagnostic domain for similar-code generation.
531#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
532#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
533#[serde(rename_all = "kebab-case")]
534pub enum SimilarCodeDiagnosticDomain {
535    /// Source discovery or workspace interpretation.
536    Workspace,
537    /// Function extraction or normalization.
538    Extraction,
539    /// Local provider execution or protocol validation.
540    Provider,
541    /// Local vector cache handling.
542    Cache,
543    /// Optional source-grounded enrichment.
544    Enrichment,
545    /// Verdict matching and review-key rebinding.
546    Review,
547}
548
549/// Actionable diagnostic without a severity or gate implication.
550#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
551#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
552pub struct SimilarCodeDiagnostic {
553    /// Stable diagnostic domain.
554    pub domain: SimilarCodeDiagnosticDomain,
555    /// Stable machine-readable code.
556    pub code: String,
557    /// Bounded human-readable explanation.
558    pub message: String,
559    /// Optional project-root-relative path.
560    pub path: Option<String>,
561}
562
563/// Raw `fallow similar-code --format json` output.
564#[derive(Debug, Clone, Serialize)]
565#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
566pub struct SimilarCodeOutput {
567    /// Independent envelope schema version.
568    pub schema_version: SimilarCodeSchemaVersion,
569    /// Fallow version that produced this output.
570    pub version: ToolVersion,
571    /// End-to-end elapsed milliseconds.
572    pub elapsed_ms: ElapsedMs,
573    /// Immutable provider, model, and parameter provenance.
574    pub generation: SimilarCodeGeneration,
575    /// Deterministically ordered unverified candidates.
576    pub candidates: Vec<SimilarCodeCandidate>,
577    /// Typed completion and boundedness accounting.
578    pub completion: SimilarCodeCompletion,
579    /// Non-severity diagnostics in deterministic order.
580    pub diagnostics: Vec<SimilarCodeDiagnostic>,
581}
582
583#[derive(Deserialize)]
584#[serde(deny_unknown_fields)]
585struct SimilarCodeOutputWire {
586    schema_version: SimilarCodeSchemaVersion,
587    version: String,
588    elapsed_ms: u64,
589    generation: SimilarCodeGeneration,
590    candidates: Vec<SimilarCodeCandidate>,
591    completion: SimilarCodeCompletion,
592    diagnostics: Vec<SimilarCodeDiagnostic>,
593}
594
595impl<'de> Deserialize<'de> for SimilarCodeOutput {
596    fn deserialize<Deserializer>(deserializer: Deserializer) -> Result<Self, Deserializer::Error>
597    where
598        Deserializer: serde::Deserializer<'de>,
599    {
600        let wire = SimilarCodeOutputWire::deserialize(deserializer)?;
601        Ok(Self {
602            schema_version: wire.schema_version,
603            version: ToolVersion(wire.version),
604            elapsed_ms: ElapsedMs(wire.elapsed_ms),
605            generation: wire.generation,
606            candidates: wire.candidates,
607            completion: wire.completion,
608            diagnostics: wire.diagnostics,
609        })
610    }
611}
612
613/// Bounded handoff for inspecting one immutable discovery candidate without
614/// rerunning global retrieval or ranking.
615#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
616#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
617#[serde(deny_unknown_fields)]
618pub struct SimilarCodeCandidateSnapshot {
619    /// Schema version of the discovery envelope that produced the candidate.
620    pub schema_version: SimilarCodeSchemaVersion,
621    /// Immutable provider, model, parameter, and scope provenance.
622    pub generation: SimilarCodeGeneration,
623    /// The exact unverified candidate selected from discovery.
624    pub candidate: SimilarCodeCandidate,
625    /// Original discovery completeness and limit accounting.
626    pub completion: SimilarCodeCompletion,
627    /// Original non-severity discovery diagnostics.
628    pub diagnostics: Vec<SimilarCodeDiagnostic>,
629}
630
631#[cfg(feature = "schema")]
632impl SimilarCodeCandidateSnapshot {
633    /// JSON Schema of the inspect handoff object, published as the
634    /// `fallow://schema/similar-code-snapshot` MCP resource so the
635    /// `inspect_similar_code` input schema can accept a plain object instead
636    /// of inlining this shape on every `tools/list`.
637    #[must_use]
638    pub fn json_schema() -> serde_json::Value {
639        serde_json::to_value(schemars::schema_for!(SimilarCodeCandidateSnapshot))
640            .unwrap_or_default()
641    }
642}
643
644/// One named graph reference used in an inspect packet.
645#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
646#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
647pub struct SimilarCodeNamedReference {
648    /// Project-root-relative, forward-slash path.
649    pub path: String,
650    /// Referenced symbol name.
651    pub name: String,
652    /// One-based source line.
653    pub line: u32,
654}
655
656/// Conservative syntactic side-effect hint for an inspected function.
657#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
658#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
659#[serde(rename_all = "kebab-case")]
660pub enum SimilarCodeSideEffectHint {
661    /// No syntactic side-effect signal was found.
662    PureLooking,
663    /// The function contains a syntactic side-effect signal.
664    MayHaveSideEffects,
665    /// The available evidence is insufficient to classify the function.
666    Unknown,
667}
668
669/// Bounded evidence for one side of an inspect packet.
670#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
671#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
672pub struct SimilarCodeSideEvidence {
673    /// Bounded source window included only in inspect output, never raw output or cache.
674    pub source_window: Option<String>,
675    /// Declared parameter count when extraction supplied it.
676    pub parameter_count: Option<u32>,
677    /// Whether the inspected function is declared async.
678    pub is_async: Option<bool>,
679    /// Whether the inspected function is a generator.
680    pub is_generator: Option<bool>,
681    /// Whether the inspected function contains an await expression.
682    pub has_await: Option<bool>,
683    /// Whether the inspected function contains a throw expression.
684    pub has_throw: Option<bool>,
685    /// Conservative syntactic side-effect classification.
686    pub side_effect_hint: Option<SimilarCodeSideEffectHint>,
687    /// Whether the function is reachable from a configured entry point.
688    pub entry_point_reachable: Option<bool>,
689    /// Bounded, deterministically ordered direct callers.
690    pub callers: Vec<SimilarCodeNamedReference>,
691    /// Bounded, deterministically ordered direct callees.
692    pub callees: Vec<SimilarCodeNamedReference>,
693    /// Bounded, deterministically ordered ownership labels.
694    pub owners: Vec<String>,
695    /// Recent commit count in the configured churn window.
696    pub churn_commits: Option<u64>,
697    /// Bounded, root-relative related test paths.
698    pub tests: Vec<String>,
699    /// Fraction covered by deterministic clone groups, from zero through one.
700    pub deterministic_clone_coverage: Option<f64>,
701    /// Runtime observation count when compatible runtime evidence is present.
702    pub runtime_observations: Option<u64>,
703}
704
705/// Bounded source-grounded packet for one immutable candidate.
706#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
707#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
708pub struct SimilarCodeInspectPacket {
709    /// Candidate identity this packet describes.
710    pub candidate_id: String,
711    /// Content-stable review key this packet describes.
712    pub review_key: String,
713    /// Availability of every optional evidence source.
714    pub availability: SimilarCodeEnrichmentAvailability,
715    /// Graph relationship label, when relationship evidence is available.
716    pub graph_relationship: Option<String>,
717    /// Evidence for the candidate's first location.
718    pub left: SimilarCodeSideEvidence,
719    /// Evidence for the candidate's second location.
720    pub right: SimilarCodeSideEvidence,
721}
722
723/// `fallow similar-code inspect --format json` output.
724#[derive(Debug, Clone, Serialize)]
725#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
726pub struct SimilarCodeInspectOutput {
727    /// Independent inspect envelope schema version.
728    pub schema_version: SimilarCodeInspectSchemaVersion,
729    /// Fallow version that produced this output.
730    pub version: ToolVersion,
731    /// End-to-end elapsed milliseconds.
732    pub elapsed_ms: ElapsedMs,
733    /// Generation provenance copied from the candidate document.
734    pub generation: SimilarCodeGeneration,
735    /// Immutable raw candidate being inspected.
736    pub candidate: SimilarCodeCandidate,
737    /// Bounded source-grounded inspect packet.
738    pub packet: SimilarCodeInspectPacket,
739    /// Typed completion and boundedness accounting.
740    pub completion: SimilarCodeCompletion,
741    /// Non-severity diagnostics in deterministic order.
742    pub diagnostics: Vec<SimilarCodeDiagnostic>,
743}
744
745#[derive(Deserialize)]
746#[serde(deny_unknown_fields)]
747struct SimilarCodeInspectOutputWire {
748    schema_version: SimilarCodeInspectSchemaVersion,
749    version: String,
750    elapsed_ms: u64,
751    generation: SimilarCodeGeneration,
752    candidate: SimilarCodeCandidate,
753    packet: SimilarCodeInspectPacket,
754    completion: SimilarCodeCompletion,
755    diagnostics: Vec<SimilarCodeDiagnostic>,
756}
757
758impl<'de> Deserialize<'de> for SimilarCodeInspectOutput {
759    fn deserialize<Deserializer>(deserializer: Deserializer) -> Result<Self, Deserializer::Error>
760    where
761        Deserializer: serde::Deserializer<'de>,
762    {
763        let wire = SimilarCodeInspectOutputWire::deserialize(deserializer)?;
764        Ok(Self {
765            schema_version: wire.schema_version,
766            version: ToolVersion(wire.version),
767            elapsed_ms: ElapsedMs(wire.elapsed_ms),
768            generation: wire.generation,
769            candidate: wire.candidate,
770            packet: wire.packet,
771            completion: wire.completion,
772            diagnostics: wire.diagnostics,
773        })
774    }
775}
776
777/// Separate verdict input for one immutable candidate.
778#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
779#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
780#[serde(deny_unknown_fields)]
781pub struct SimilarCodeVerdict {
782    /// Snapshot identity from the raw candidate.
783    pub candidate_id: String,
784    /// Content-stable identity from the raw candidate.
785    pub review_key: String,
786    /// Whether the pair is useful enough to review. Null means undecided.
787    pub candidate_worthy: Option<bool>,
788    /// Whether the two functions behave equivalently. Null means undecided.
789    pub behaviorally_equivalent: Option<bool>,
790    /// Whether consolidation is safe. Null means undecided.
791    pub refactor_safe: Option<bool>,
792    /// Domain interpretation independent of the three judgments.
793    pub outcome: SimilarCodeDomainOutcome,
794    /// Bounded explanation grounded in the inspected sources.
795    pub rationale: String,
796}
797
798impl SimilarCodeVerdict {
799    /// Validate the implication chain without collapsing the three judgments.
800    ///
801    /// # Errors
802    ///
803    /// Returns an error when a positive stronger judgment contradicts a
804    /// negative or unknown prerequisite.
805    pub fn validate(&self) -> Result<(), SimilarCodeVerdictValidationError> {
806        if self.refactor_safe == Some(true) && self.behaviorally_equivalent != Some(true) {
807            return Err(SimilarCodeVerdictValidationError::RefactorSafetyRequiresEquivalence);
808        }
809        if self.behaviorally_equivalent == Some(true) && self.candidate_worthy != Some(true) {
810            return Err(SimilarCodeVerdictValidationError::EquivalenceRequiresCandidate);
811        }
812        Ok(())
813    }
814}
815
816/// Validation failures for the independent verdict judgments.
817#[derive(Debug, Clone, Copy, PartialEq, Eq)]
818pub enum SimilarCodeVerdictValidationError {
819    /// Refactor safety cannot be positive without behavioral equivalence.
820    RefactorSafetyRequiresEquivalence,
821    /// Behavioral equivalence cannot be positive for a rejected candidate.
822    EquivalenceRequiresCandidate,
823}
824
825impl fmt::Display for SimilarCodeVerdictValidationError {
826    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
827        match self {
828            Self::RefactorSafetyRequiresEquivalence => {
829                formatter.write_str("refactor_safe=true requires behaviorally_equivalent=true")
830            }
831            Self::EquivalenceRequiresCandidate => {
832                formatter.write_str("behaviorally_equivalent=true requires candidate_worthy=true")
833            }
834        }
835    }
836}
837
838impl std::error::Error for SimilarCodeVerdictValidationError {}
839
840/// Domain outcome assigned by review, never by candidate generation.
841#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
842#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
843#[serde(rename_all = "kebab-case")]
844pub enum SimilarCodeDomainOutcome {
845    /// Functions implement the same responsibility.
846    SameResponsibility,
847    /// Functions are related but intentionally serve distinct responsibilities.
848    RelatedButDistinct,
849    /// Duplication is understood and intentional.
850    IntentionalDuplication,
851    /// Pair is not meaningfully related.
852    Unrelated,
853    /// Available evidence does not support a verdict.
854    NeedsHumanReview,
855}
856
857/// Versioned external verdict document consumed by review.
858#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
859#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
860#[serde(deny_unknown_fields)]
861pub struct SimilarCodeVerdictInput {
862    /// Independent verdict document schema version.
863    pub schema_version: SimilarCodeVerdictSchemaVersion,
864    /// Verdicts in deterministic candidate order.
865    pub verdicts: Vec<SimilarCodeVerdict>,
866}
867
868/// How review matched an external verdict to the current candidate.
869#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
870#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
871#[serde(rename_all = "kebab-case")]
872pub enum SimilarCodeVerdictMatch {
873    /// Verdict matched the exact snapshot candidate identity.
874    CandidateId,
875    /// Verdict was rebound unambiguously through both content digests.
876    ReviewKey,
877    /// No verdict was supplied for this candidate.
878    Unverified,
879    /// Review-key rebinding was ambiguous and therefore refused.
880    AmbiguousReviewKey,
881}
882
883/// One candidate joined with its separate verdict, if safely matched.
884#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
885#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
886pub struct SimilarCodeReviewedCandidate {
887    /// Immutable raw candidate, unchanged by review.
888    pub candidate: SimilarCodeCandidate,
889    /// Safely matched external verdict, absent when still unverified.
890    pub verdict: Option<SimilarCodeVerdict>,
891    /// Match or abstention path used by review.
892    pub verdict_match: SimilarCodeVerdictMatch,
893    /// Domain outcome. Unverified or ambiguous entries use `needs-human-review`.
894    pub outcome: SimilarCodeDomainOutcome,
895}
896
897/// Digests that make the review join reproducible without exposing source.
898#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
899#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
900pub struct SimilarCodeReviewProvenance {
901    /// SHA-256 digest of the exact candidate JSON input bytes.
902    pub candidates_sha256: String,
903    /// SHA-256 digest of the exact verdict JSON input bytes.
904    pub verdicts_sha256: String,
905}
906
907/// `fallow similar-code review --format json` output.
908#[derive(Debug, Clone, Serialize)]
909#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
910pub struct SimilarCodeReviewOutput {
911    /// Independent review envelope schema version.
912    pub schema_version: SimilarCodeReviewSchemaVersion,
913    /// Fallow version that produced this output.
914    pub version: ToolVersion,
915    /// End-to-end elapsed milliseconds.
916    pub elapsed_ms: ElapsedMs,
917    /// Immutable generation provenance copied from the candidate document.
918    pub generation: SimilarCodeGeneration,
919    /// Input document provenance for this deterministic join.
920    pub review: SimilarCodeReviewProvenance,
921    /// Raw candidates joined with verdicts in candidate order.
922    pub candidates: Vec<SimilarCodeReviewedCandidate>,
923    /// Typed completion and boundedness accounting.
924    pub completion: SimilarCodeCompletion,
925    /// Non-severity diagnostics in deterministic order.
926    pub diagnostics: Vec<SimilarCodeDiagnostic>,
927}
928
929#[derive(Deserialize)]
930#[serde(deny_unknown_fields)]
931struct SimilarCodeReviewOutputWire {
932    schema_version: SimilarCodeReviewSchemaVersion,
933    version: String,
934    elapsed_ms: u64,
935    generation: SimilarCodeGeneration,
936    review: SimilarCodeReviewProvenance,
937    candidates: Vec<SimilarCodeReviewedCandidate>,
938    completion: SimilarCodeCompletion,
939    diagnostics: Vec<SimilarCodeDiagnostic>,
940}
941
942impl<'de> Deserialize<'de> for SimilarCodeReviewOutput {
943    fn deserialize<Deserializer>(deserializer: Deserializer) -> Result<Self, Deserializer::Error>
944    where
945        Deserializer: serde::Deserializer<'de>,
946    {
947        let wire = SimilarCodeReviewOutputWire::deserialize(deserializer)?;
948        Ok(Self {
949            schema_version: wire.schema_version,
950            version: ToolVersion(wire.version),
951            elapsed_ms: ElapsedMs(wire.elapsed_ms),
952            generation: wire.generation,
953            review: wire.review,
954            candidates: wire.candidates,
955            completion: wire.completion,
956            diagnostics: wire.diagnostics,
957        })
958    }
959}
960
961/// Serialize raw similar-code output with its root discriminator.
962///
963/// # Errors
964///
965/// Returns a serde error when the envelope cannot be converted to JSON.
966pub fn serialize_similar_code_json_output(
967    output: SimilarCodeOutput,
968    mode: RootEnvelopeMode,
969) -> Result<serde_json::Value, serde_json::Error> {
970    serialize_named_json_output(output, "similar-code", mode)
971}
972
973/// Serialize a similar-code inspect packet with its root discriminator.
974///
975/// # Errors
976///
977/// Returns a serde error when the envelope cannot be converted to JSON.
978pub fn serialize_similar_code_inspect_json_output(
979    output: SimilarCodeInspectOutput,
980    mode: RootEnvelopeMode,
981) -> Result<serde_json::Value, serde_json::Error> {
982    serialize_named_json_output(output, "similar-code-inspect", mode)
983}
984
985/// Serialize reviewed similar-code output with its root discriminator.
986///
987/// # Errors
988///
989/// Returns a serde error when the envelope cannot be converted to JSON.
990pub fn serialize_similar_code_review_json_output(
991    output: SimilarCodeReviewOutput,
992    mode: RootEnvelopeMode,
993) -> Result<serde_json::Value, serde_json::Error> {
994    serialize_named_json_output(output, "similar-code-review", mode)
995}
996
997/// Serialize local-provider status with its root discriminator.
998///
999/// # Errors
1000///
1001/// Returns a serde error when the envelope cannot be converted to JSON.
1002pub fn serialize_similar_code_status_json_output(
1003    output: SimilarCodeStatusOutput,
1004    mode: RootEnvelopeMode,
1005) -> Result<serde_json::Value, serde_json::Error> {
1006    serialize_named_json_output(output, "similar-code-status", mode)
1007}
1008
1009/// Serialize a vector-cache clear result with its root discriminator.
1010///
1011/// # Errors
1012///
1013/// Returns a serde error when the envelope cannot be converted to JSON.
1014pub fn serialize_similar_code_cache_clear_json_output(
1015    output: SimilarCodeCacheClearOutput,
1016    mode: RootEnvelopeMode,
1017) -> Result<serde_json::Value, serde_json::Error> {
1018    serialize_named_json_output(output, "similar-code-cache-clear", mode)
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023    use super::*;
1024
1025    #[test]
1026    fn verdict_axes_remain_independent_but_enforce_implication() {
1027        let verdict = SimilarCodeVerdict {
1028            candidate_id: "sc_123".to_string(),
1029            review_key: "scr_456".to_string(),
1030            candidate_worthy: Some(true),
1031            behaviorally_equivalent: Some(false),
1032            refactor_safe: Some(true),
1033            outcome: SimilarCodeDomainOutcome::RelatedButDistinct,
1034            rationale: "Same domain, different behavior.".to_string(),
1035        };
1036
1037        assert_eq!(
1038            verdict.validate(),
1039            Err(SimilarCodeVerdictValidationError::RefactorSafetyRequiresEquivalence)
1040        );
1041    }
1042
1043    #[test]
1044    fn raw_serializer_adds_only_the_similar_code_kind() {
1045        let output = raw_output();
1046
1047        let value = serialize_similar_code_json_output(output, RootEnvelopeMode::Tagged)
1048            .expect("similar-code output should serialize");
1049
1050        assert_eq!(value["kind"], "similar-code");
1051        assert_eq!(value["schema_version"], "1");
1052        assert!(value.get("severity").is_none());
1053        assert!(value.get("gate").is_none());
1054        assert!(value.get("fixes").is_none());
1055    }
1056
1057    #[test]
1058    fn raw_output_round_trips_for_review_input() {
1059        let value = serde_json::to_value(raw_output()).expect("raw output should serialize");
1060        let decoded: SimilarCodeOutput =
1061            serde_json::from_value(value).expect("raw output should deserialize");
1062
1063        assert_eq!(decoded.schema_version, SimilarCodeSchemaVersion::V1);
1064        assert_eq!(decoded.version.0, "3.9.0");
1065        assert_eq!(
1066            decoded.completion.status,
1067            SimilarCodeCompletionStatus::Complete
1068        );
1069    }
1070
1071    #[test]
1072    fn verdict_input_rejects_unknown_fields() {
1073        let value = serde_json::json!({
1074            "schema_version": "1",
1075            "verdicts": [],
1076            "unexpected": true
1077        });
1078
1079        assert!(serde_json::from_value::<SimilarCodeVerdictInput>(value).is_err());
1080    }
1081
1082    #[test]
1083    fn review_preserves_null_judgments() {
1084        let verdict = SimilarCodeVerdict {
1085            candidate_id: "sc_123".to_string(),
1086            review_key: "scr_456".to_string(),
1087            candidate_worthy: None,
1088            behaviorally_equivalent: None,
1089            refactor_safe: None,
1090            outcome: SimilarCodeDomainOutcome::NeedsHumanReview,
1091            rationale: "Insufficient evidence.".to_string(),
1092        };
1093
1094        let value = serde_json::to_value(verdict).expect("verdict should serialize");
1095        assert!(value["candidate_worthy"].is_null());
1096        assert!(value["behaviorally_equivalent"].is_null());
1097        assert!(value["refactor_safe"].is_null());
1098    }
1099
1100    fn generation() -> SimilarCodeGeneration {
1101        SimilarCodeGeneration {
1102            extraction_semantics_version: 1,
1103            embedding_semantics_version: 1,
1104            provider: SimilarCodeProviderProvenance {
1105                provider: SimilarCodeProvider::OfficialLocalCompanion,
1106                companion_version: "3.9.0".to_string(),
1107                protocol_version: 2,
1108                source_left_machine: false,
1109            },
1110            model: SimilarCodeModelProvenance {
1111                model_id: "example/model".to_string(),
1112                revision: "immutable-revision".to_string(),
1113                artifact_sha256: "abc".to_string(),
1114                license: "Apache-2.0".to_string(),
1115                dimensions: 384,
1116            },
1117            parameters: SimilarCodeGenerationParameters {
1118                dtype: "fp32".to_string(),
1119                pooling: "mean".to_string(),
1120                normalized: true,
1121                batch_size: 8,
1122                max_tokens: 1024,
1123                parameter_sha256: "def".to_string(),
1124            },
1125            scope: SimilarCodeScopeProvenance {
1126                active: true,
1127                paths: vec!["src/a.ts".to_string()],
1128            },
1129            threshold: 0.8,
1130            min_lines: 3,
1131        }
1132    }
1133
1134    fn raw_output() -> SimilarCodeOutput {
1135        SimilarCodeOutput {
1136            schema_version: SimilarCodeSchemaVersion::V1,
1137            version: ToolVersion("3.9.0".to_string()),
1138            elapsed_ms: ElapsedMs(4),
1139            generation: generation(),
1140            candidates: Vec::new(),
1141            completion: completion(),
1142            diagnostics: Vec::new(),
1143        }
1144    }
1145
1146    fn completion() -> SimilarCodeCompletion {
1147        SimilarCodeCompletion {
1148            status: SimilarCodeCompletionStatus::Complete,
1149            phases: Vec::new(),
1150            limits: SimilarCodeLimits {
1151                max_files: 10,
1152                max_functions: 100,
1153                max_source_bytes: 1_000_000,
1154                max_function_bytes: 10_000,
1155                max_batch_size: 8,
1156                max_vector_bytes: 1_000_000,
1157                max_comparisons: 1_000,
1158                max_candidates: 10,
1159                max_neighbors_per_function: 3,
1160                timeout_ms: 30_000,
1161            },
1162            skips: Vec::new(),
1163            cache: SimilarCodeCacheSummary {
1164                status: SimilarCodeCacheStatus::Miss,
1165                hits: 0,
1166                misses: 0,
1167                writes: 0,
1168                invalid_entries: 0,
1169            },
1170            provider_inference_ms: 0,
1171        }
1172    }
1173}