1#![deny(unsafe_code)]
8
9use std::sync::OnceLock;
10
11mod remote_scoped_launch;
12pub use remote_scoped_launch::REMOTE_PROCESS_IDENTITY_SCRIPT;
14
15pub mod admission_recommendation;
16pub mod admission_rejection;
17pub mod admit_preflight;
18pub mod api;
19pub mod artifact_cost;
20pub mod artifact_pattern;
21pub mod artifact_verify;
22pub mod binary_hash;
23pub mod bypass_record;
24pub mod bypass_recovery;
25pub mod capability_probe;
26pub mod cargo_path_deps;
27pub mod classifier_drift;
28pub mod closure_explain;
29pub mod config;
30pub mod dependency_closure_planner;
31pub mod discovery;
32pub mod disk_pressure_report;
33pub mod disk_reclaim;
34pub mod e2e;
35pub mod errors;
36pub mod exec_misuse;
37pub mod exec_policy;
38pub mod exec_response;
39pub mod fleet_diff;
40pub mod fleet_provenance;
41pub mod fleet_smoke_profile;
42pub mod fleet_status;
43pub mod force_resync;
44pub mod gc_roots;
45pub mod hooks;
46pub mod incident;
47pub mod incident_ledger;
48pub mod isolated_staging;
49pub mod job_identity;
50pub mod job_recovery;
51pub mod log_retention;
52pub mod logging;
53pub mod mock;
54pub mod mock_worker;
55pub mod orphan_quarantine;
56pub mod path_topology;
57pub mod patterns;
58#[cfg(test)]
59mod patterns_security_test;
60pub mod placement;
61pub mod pnpm_workspace;
62pub mod pooled_target_key;
63pub mod process_identity;
64pub mod proof_handoff;
65pub mod proof_intent;
66pub mod proof_policy;
67pub mod proof_replay;
68#[cfg(test)]
69mod proptest_tests;
70pub mod protocol;
71pub mod queue_contract;
72pub mod readiness;
73pub mod redaction;
74pub mod remediation_config;
75pub mod remediation_view;
76#[cfg(unix)]
77pub mod remote_compilation;
78#[cfg(unix)]
79pub mod remote_verification;
80pub mod repo_updater_contract;
81pub mod rsync_flavor;
82pub mod schema_versions;
83#[cfg(unix)]
84pub mod ssh;
85#[cfg(all(test, unix))]
86mod ssh_timeout_test;
87pub mod ssh_utils;
88pub mod stale_target_reap;
89pub mod storm_control;
90pub mod telemetry_explain;
91pub mod telemetry_freshness;
92pub mod test_change;
93pub mod testing;
94pub mod tool_requirement;
95pub mod toolchain;
96pub mod transfer_hardening;
97pub mod types;
98pub mod ui;
99pub mod util;
100pub mod worker_config_validation;
101pub mod worker_facts;
102
103pub const BUILD_COMMIT_ENV_VARS: &[&str] = &[
104 "RCH_GIT_COMMIT",
105 "VERGEN_GIT_SHA",
106 "GIT_COMMIT",
107 "GITHUB_SHA",
108];
109
110pub fn build_commit() -> Option<&'static str> {
116 first_build_commit([
117 option_env!("RCH_GIT_COMMIT"),
118 option_env!("VERGEN_GIT_SHA"),
119 option_env!("GIT_COMMIT"),
120 option_env!("GITHUB_SHA"),
121 ])
122}
123
124fn first_build_commit<'a>(
125 candidates: impl IntoIterator<Item = Option<&'a str>>,
126) -> Option<&'a str> {
127 candidates
128 .into_iter()
129 .flatten()
130 .map(str::trim)
131 .find(|value| is_build_commit_stamp(value))
132}
133
134fn is_build_commit_stamp(value: &str) -> bool {
137 fn is_commit_hash(value: &str) -> bool {
138 (7..=64).contains(&value.len()) && value.bytes().all(|byte| byte.is_ascii_hexdigit())
139 }
140 is_commit_hash(value)
141 || value.strip_suffix("-dirty").is_some_and(is_commit_hash)
142 || value
143 .split_once("-overlay-")
144 .is_some_and(|(commit, fingerprint)| {
145 is_commit_hash(commit)
146 && fingerprint.len() == 64
147 && fingerprint.bytes().all(|byte| byte.is_ascii_hexdigit())
148 })
149}
150
151pub fn build_version_value() -> String {
152 build_version_value_with_commit(env!("CARGO_PKG_VERSION"), build_commit())
153}
154
155pub fn build_version_value_static() -> &'static str {
156 static VERSION_VALUE: OnceLock<String> = OnceLock::new();
157
158 VERSION_VALUE.get_or_init(build_version_value).as_str()
159}
160
161pub fn build_version_value_with_commit(package_version: &str, commit: Option<&str>) -> String {
162 let mut value = package_version.to_string();
163
164 if let Some(commit) = commit.map(str::trim).filter(|value| !value.is_empty()) {
165 value.push_str(" (commit ");
166 let (revision, suffix) = commit
167 .split_once('-')
168 .map_or((commit, None), |(revision, suffix)| {
169 (revision, Some(suffix))
170 });
171 value.push_str(short_commit(revision));
172 if let Some(suffix) = suffix {
173 value.push('-');
174 value.push_str(suffix);
175 }
176 value.push(')');
177 }
178
179 value
180}
181
182fn short_commit(commit: &str) -> &str {
183 commit
184 .char_indices()
185 .nth(12)
186 .map_or(commit, |(index, _)| &commit[..index])
187}
188
189pub use artifact_cost::{
190 ArtifactCostReport, ArtifactPhaseTimings, GlobAdvice, RetrievalMode, assess_glob_expansion,
191 compute_artifact_cost, is_broad_recursive_glob,
192};
193pub use artifact_pattern::{
194 ARTIFACT_OUTPUT_ROOTS, ArtifactPatternRewrite, ArtifactRetrievalDiagnostics,
195 artifact_dest_is_safe, rewrite_artifact_pattern,
196};
197pub use artifact_verify::{
198 ArtifactManifest, FileHash, VerificationFailure, VerificationResult, compute_file_hash,
199 create_manifest, verify_artifacts,
200};
201pub use binary_hash::{
202 BinaryHashResult, binaries_equivalent, binary_contains_marker, compute_binary_hash,
203};
204pub use bypass_record::{
205 AutoRejoinCriteria, BypassBackoff, BypassFailureClass, BypassRecord, BypassRecordStore,
206 BypassState, DisabledMigration, DisabledWorkerSnapshot, MAX_DIAGNOSTIC_CHARS,
207 bypass_record_schema, bypass_record_schema_version, classify_disable_reason,
208 default_bypass_record_path, migrate_disabled_worker, truncate_diagnostic,
209};
210pub use bypass_recovery::{
211 CanaryDecision, CanaryOutcome, ProbeDecision, RecoveryProbe, decide_canary, decide_probe,
212};
213pub use cargo_path_deps::{
214 CargoPathDependencyEdge, CargoPathDependencyError, CargoPathDependencyErrorKind,
215 CargoPathDependencyGraph, CargoPathDependencyPackage, CargoPathMaterializationClosure,
216 resolve_cargo_path_dependency_graph, resolve_cargo_path_dependency_graph_with_policy,
217 resolve_cargo_path_materialization_closure,
218 resolve_cargo_path_materialization_closure_with_policy,
219};
220pub use closure_explain::{
221 ClosureExplainEntry, ClosureExplainReport, RootConvergence, RootSyncOutcome, derive_outcome,
222 explain_closure,
223};
224pub use dependency_closure_planner::{
225 DependencyClosurePlan, DependencyClosurePlanState, DependencyPlanIssue, DependencyRiskClass,
226 DependencySyncAction, DependencySyncMetadata, DependencySyncReason,
227 build_dependency_closure_plan, build_dependency_closure_plan_with_policy,
228 plan_dependency_closure_from_graph,
229};
230pub use force_resync::{
231 ForceResyncPlan, ForceResyncReport, InvalidationAction, RefusedInvalidation, ResyncOutcome,
232 StaleRoot, apply_force_resync, is_safe_invalidation_target, plan_force_resync,
233};
234pub use gc_roots::{
235 RCH_CARGO_CACHE_PREFIX, RCH_CARGO_HOME_BASE_VAR, RCH_CARGO_HOME_PREFIX,
236 remote_cargo_cache_expr, remote_cargo_home_base_prelude, remote_cargo_home_expr,
237};
238pub use logging::{LogConfig, LogFormat, LoggingGuards, init_logging, init_logging_with_layer};
239pub use mock_worker::MockWorkerServer;
240pub use path_topology::{
241 DEFAULT_ALIAS_PROJECT_ROOT, DEFAULT_CANONICAL_PROJECT_ROOT, NormalizationDecision,
242 NormalizedProjectPath, PathNormalizationError, PathNormalizationErrorKind, PathTopologyPolicy,
243 normalize_project_path, normalize_project_path_with_policy,
244};
245pub use patterns::{
246 Classification, ClassificationDetails, ClassificationTier, CompilationKind, TierDecision,
247 classify_command, classify_command_detailed, compound_local_compilation_segments,
248 declined_compilation_due_to_structure, split_shell_commands,
249};
250pub use protocol::{HookInput, HookOutput, ToolInput};
251pub use queue_contract::{
252 AdmissionState, QueueContractOutcome, QueueContractResponse, QueueOptions, StartState,
253 WaitResult, resolve_queue_contract,
254};
255pub use repo_updater_contract::{
256 MockRepoUpdaterAdapter, REPO_UPDATER_ALIAS_PROJECTS_ROOT, REPO_UPDATER_CANONICAL_PROJECTS_ROOT,
257 REPO_UPDATER_CONTRACT_SCHEMA_VERSION, REPO_UPDATER_DEFAULT_BINARY,
258 REPO_UPDATER_MIN_SUPPORTED_VERSION, RepoUpdaterAdapter, RepoUpdaterAdapterCommand,
259 RepoUpdaterAdapterContract, RepoUpdaterAdapterRequest, RepoUpdaterAdapterResponse,
260 RepoUpdaterFailure, RepoUpdaterFailureKind, RepoUpdaterFallbackMode,
261 RepoUpdaterIdempotencyGuarantee, RepoUpdaterJsonEnvelope, RepoUpdaterOutputFormat,
262 RepoUpdaterResponseStatus, RepoUpdaterVersionCompatibility, RepoUpdaterVersionPolicy,
263 build_invocation, classify_exit_code, evaluate_version_compatibility,
264 map_failure_kind_to_error_code, repo_updater_envelope_schema, repo_updater_request_schema,
265 repo_updater_response_schema,
266};
267pub use ssh_utils::{
269 CommandResult, EnvPrefix, build_env_prefix, is_retryable_transport_error,
270 is_retryable_transport_error_text, shell_escape_value,
271};
272#[cfg(unix)]
274pub use ssh::{KnownHostsPolicy, SshClient, SshOptions, SshPool};
275pub use test_change::{TestChangeGuard, TestCodeChange};
276pub use toolchain::{ToolchainInfo, wrap_command_with_color, wrap_command_with_toolchain};
277pub use transfer_hardening::{
278 FirstFailure, RetryDecision, RsyncFailureClass, classify_rsync_outcome, is_ephemeral_path,
279};
280pub use types::{
281 AffinityConfig, ApiConfig, BoxRole, BuildCancellationMetadata, BuildCancellationWorkerHealth,
282 BuildHeartbeatPhase, BuildHeartbeatRequest, BuildLocation, BuildRecord, BuildStats,
283 CircuitBreakerConfig, CircuitState, CircuitStats, ColorMode, CommandPriority,
284 CommandTimingBreakdown, CompilationConfig, CompilationMetrics, CompilationTimer,
285 CompilationTimingBreakdown, DashboardConfig, DoctorConfig, DoctorWebhookEndpoint,
286 DoctorWebhookFormat, DoctorWebhooksConfig, EnvironmentConfig, ExecutionConfig, FairnessConfig,
287 FleetConfig, GeneralConfig, MetricsAggregator, OS_TAG_PREFIX, OutputConfig, OutputVisibility,
288 PathTopologyConfig, RchConfig, ReleaseRequest, RemoteBuildJobs, RequiredRuntime, RetryConfig,
289 SELECTION_RESPONSE_PROTOCOL_VERSION, SavedTimeStats, SelectedWorker, SelectionConfig,
290 SelectionDiagnostics, SelectionReason, SelectionRequest, SelectionResponse, SelectionStrategy,
291 SelectionWeightConfig, SelfHealingConfig, SelfHealingLogLevel, SelfTestConfig,
292 SelfTestFailureAction, SelfTestWorkers, TransferConfig, WorkerCapabilities, WorkerConfig,
293 WorkerId, WorkerSelectionDiagnostic, WorkerSelectionDiagnosticDecision, WorkerStatus,
294 declared_os, default_socket_path, os_tag, validate_remote_base,
295 x86_64_microarch_level_from_flags,
296};
297
298pub use testing::{TestLogEntry, TestLogger, TestPhase, TestResult};
300
301pub use config::{
303 ConfigSource, ConfigValueSource, ConfigWarning, EnvError, EnvParser, Profile, Severity,
304 Sourced, validate_config,
305};
306
307pub use discovery::{
309 DiscoveredHost, DiscoverySource, discover_all, parse_shell_aliases,
310 parse_shell_aliases_content, parse_ssh_config, parse_ssh_config_content,
311};
312
313pub use ui::{
315 ErrorPanel, ErrorSeverity, Icons, IntoErrorPanel, OutputContext, RchTheme, ResultExt,
316 anyhow_to_json, anyhow_to_panel, display_anyhow_error, display_error, display_error_with_code,
317 error_to_json, error_to_panel,
318};
319
320pub use capability_probe::{
322 CapabilityRequirement, CapabilityVerdict, EligibilityVerdict, ProbeSpec, ProbedFacts,
323 WorkerLiveness, assess_admissibility, assess_worker_eligibility, build_capability_probe_script,
324 parse_capability_probe, remote_worker_binary_path,
325};
326pub use incident::{
327 ControlState, IncidentEvent, IncidentEventType, IncidentReasonCode, IncidentSource,
328 SelectedMode, incident_schema_version,
329};
330pub use incident_ledger::{
331 IncidentFilter, IncidentLedger, IncidentLedgerConfig, LedgerReadStats, default_ledger_path,
332};
333pub use placement::{
334 ControlDiagnostic, ControlDiagnosticLevel, ControlKind, PlacementControl, PlacementPlan,
335 QueuePolicy, RequestedWorkerFacts, RequestedWorkerOutcome, RequestedWorkerStatus,
336 StrictRemotePolicy, TargetDirPolicy, VisibilityMode, evaluate_requested_worker,
337 placement_controls, resolve_placement,
338};
339pub use proof_intent::{
340 ProofIntent, ProofIntentStore, ReplayConstraints, ReplayContext, ReplayDecision,
341 SourceFingerprint, StaleSourcePolicy, derive_intent_id, proof_intent_schema,
342 proof_intent_schema_version, validate_replay,
343};
344pub use telemetry_explain::{
345 ProbeOutcome, TelemetrySignals, TelemetryUnavailabilityReason, WhyUnhealthy,
346 explain_unavailability,
347};
348pub use telemetry_freshness::{
349 FreshnessAssessment, FreshnessInputs, FreshnessVerdict, assess as assess_telemetry_freshness,
350};
351pub use worker_config_validation::{
352 ConfigDriftFinding, ConfigDriftKind, LiveHostObservation, validate_worker_config,
353};
354pub use worker_facts::{
355 DiskFacts, DiskRootFacts, HostFacts, RuntimeFacts, RustFacts, UserFacts, WorkerBinaryFacts,
356 WorkerFacts, derive_target_triple, worker_facts_schema, worker_facts_schema_version,
357};
358
359pub use errors::{
361 CodeExplanation, CodeNamespace, ErrorCategory, ErrorCode, ErrorEntry, ReliabilityCategoryKind,
362 ReliabilityReasonCode, RunbookEntry,
363};
364
365pub use schema_versions::current_version as schema_version;
367pub use schema_versions::{ALL_COMPONENTS as SCHEMA_VERSION_COMPONENTS, SchemaComponent};
368
369pub use api::{API_VERSION, ApiError, ApiResponse, ErrorContext, LegacyErrorCode};
371
372pub use hooks::{HookResult, is_claude_code_installed, verify_and_install_claude_code_hook};
374
375#[cfg(test)]
376mod build_version_tests {
377 use super::{BUILD_COMMIT_ENV_VARS, build_version_value_with_commit, first_build_commit};
378
379 #[test]
380 fn build_commit_skips_values_the_build_script_rejects() {
381 assert_eq!(
383 first_build_commit([None, None, Some("not-a-commit"), None]),
384 None
385 );
386 let hash = "0123456789abcdef0123456789abcdef01234567";
387 assert_eq!(
388 first_build_commit([Some(""), None, Some("v2.0.0-3-gdeadbee"), Some(hash)]),
389 Some(hash)
390 );
391 assert_eq!(first_build_commit([Some(" abc1234 ")]), Some("abc1234"));
392 assert_eq!(first_build_commit([Some("abc123")]), None, "too short");
393 let dirty = format!("{hash}-dirty");
394 assert_eq!(
395 first_build_commit([Some(dirty.as_str())]),
396 Some(dirty.as_str())
397 );
398 let overlay = format!("{hash}-overlay-{}", "b".repeat(64));
399 assert_eq!(
400 first_build_commit([Some(overlay.as_str())]),
401 Some(overlay.as_str())
402 );
403 let short_overlay = format!("{hash}-overlay-{}", "b".repeat(63));
404 assert_eq!(first_build_commit([Some(short_overlay.as_str())]), None);
405 }
406
407 #[test]
408 fn build_version_value_omits_missing_commit() {
409 assert_eq!(build_version_value_with_commit("1.0.24", None), "1.0.24");
410 }
411
412 #[test]
413 fn build_version_value_trims_and_shortens_commit() {
414 assert_eq!(
415 build_version_value_with_commit(
416 "1.0.24",
417 Some(" 2fa1249a18dfd05ae5e319e8d10bfd3c9ea1af55 "),
418 ),
419 "1.0.24 (commit 2fa1249a18df)"
420 );
421 }
422
423 #[test]
424 fn build_version_value_ignores_empty_commit() {
425 assert_eq!(
426 build_version_value_with_commit("1.0.24", Some(" ")),
427 "1.0.24"
428 );
429 }
430
431 #[test]
432 fn build_source_stamp_version_preserves_nonclean_suffixes() {
433 let hash = "a".repeat(40);
434 for suffix in ["dirty".to_string(), format!("overlay-{}", "b".repeat(64))] {
435 assert_eq!(
436 build_version_value_with_commit("2.0.0", Some(&format!("{hash}-{suffix}"))),
437 format!("2.0.0 (commit {}-{suffix})", "a".repeat(12))
438 );
439 }
440 }
441
442 #[test]
443 fn build_commit_env_var_order_is_documented() {
444 assert_eq!(
445 BUILD_COMMIT_ENV_VARS,
446 &[
447 "RCH_GIT_COMMIT",
448 "VERGEN_GIT_SHA",
449 "GIT_COMMIT",
450 "GITHUB_SHA"
451 ]
452 );
453 }
454}