cargoless_core/lib.rs
1//! The daemon. Owners fill these modules in against the Plane CWDL epics:
2//!
3//! - watcher — `notify` filesystem watcher (Epic 2)
4//! - analyzer — rust-analyzer subprocess + LSP client (Epic 2)
5//! - model — file-level green/red state (Epic 2, decision D4)
6//! - build — trunk-build orchestration + input hashing (Epic 3)
7//! - server — HTTP + WebSocket dev server, never-serve-red (Epic 4)
8//!
9//! Skeleton only exposes a build identifier so the workspace links and CI is
10//! green-on-empty (decision D10).
11
12pub mod activity;
13pub mod activitymgr;
14pub mod analyzer;
15pub mod barrier;
16pub mod build;
17pub mod cache_layout;
18pub mod cluster;
19pub mod clusterdrv;
20pub mod clustermgr;
21pub mod config;
22pub mod corun;
23pub mod diagnostics_store;
24pub mod idle;
25pub mod lsp;
26pub mod model;
27pub mod multiplex;
28pub mod overlay;
29pub mod procmacro;
30pub mod recovery;
31pub mod repo;
32pub mod shutdown;
33pub mod structural;
34pub mod transport;
35pub mod watcher;
36
37pub use cargoless_cas::{ContentStore, LocalDiskStore};
38pub use cargoless_proto::{
39 ArtifactMeta, BuildIdentity, BuildOutcome, BuildResult, BuildTrigger, CheckResult, ContentHash,
40 Diagnostic, FileState, InputHash, Profile, Severity, StateEvent, TargetTriple, TreeState,
41};
42pub use config::{FleetConfig, FleetConfigError, FleetOverrides, Provenance, Source};
43pub use model::LifecycleEvent;
44
45/// The single canonical identity string — `<product> <version>` — used
46/// by `--version`, `help`, AND every command banner.
47///
48/// ## §gap-3 / #89: this is the ONLY product-name site in the binary
49///
50/// Before #89 the binary rendered TWO different product names depending
51/// on the command: `--version` / `help` showed `tf-trunk <ver>` (this
52/// constant), while `watch` built its own `cargoless <ver>` banner
53/// straight off `CARGO_PKG_VERSION` in `cargoless`. Same binary, two
54/// names — dogfood-lead's §gap-3 finding. Every banner now reads THIS
55/// constant (`cargoless_core::BUILD_ID`), so the binary speaks one name.
56///
57/// **Decision D1 (CWDL-12) rename = change the `"cargoless"` literal on
58/// the next line, and nothing else.** That single-site property is the
59/// entire point of #89: it turns docs-launch-lead's D1 rename (#87)
60/// from "hunt every banner across N files, hope you got them all" into
61/// "change one literal, the type system + the
62/// `build_id_is_name_neutral` test enforce the rest".
63///
64/// The placeholder is `"cargoless"` (the working repo/binary name per
65/// CLAUDE.md) — explicitly NOT `"tf"` / `"tf-trunk"` (Terraform
66/// collision, rejected per CLAUDE.md; the old `tf-trunk` value leaked
67/// that rejected token into `--version` output).
68pub const BUILD_ID: &str = concat!("cargoless ", env!("CARGO_PKG_VERSION"));
69
70/// Returns [`BUILD_ID`]. Kept as a fn (not just the const) because
71/// callers historically bound to `cargoless_core::build_id()`; both paths now
72/// resolve to the single canonical string.
73pub fn build_id() -> &'static str {
74 BUILD_ID
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn build_id_is_single_consolidated_identity() {
83 // §gap-3 / #89: BUILD_ID is THE single product-name site. This
84 // test is the regression guard that enforces the consolidation
85 // invariants a future edit (incl. the D1 rename) must keep:
86 let id = build_id();
87
88 // 1. Non-empty `<name> <version>` shape.
89 let (name, version) = id
90 .split_once(' ')
91 .expect("BUILD_ID is `<product> <version>` (one space)");
92 assert!(!name.is_empty(), "product name must not be empty");
93 assert!(!version.is_empty(), "version must not be empty");
94
95 // 2. Carries the crate version (D1 rename must not drop it).
96 assert_eq!(
97 version,
98 env!("CARGO_PKG_VERSION"),
99 "BUILD_ID must end with the workspace package version"
100 );
101
102 // 3. Terraform-collision guard (the project-long invariant):
103 // the bare `tf` name must never be the identifier, AND the
104 // old `tf-trunk` placeholder (which leaked the rejected `tf`
105 // token) must be gone post-#89.
106 assert_ne!(name, "tf", "bare Terraform-colliding name rejected");
107 assert!(
108 !id.contains("tf-trunk"),
109 "the stale `tf-trunk` placeholder must not survive #89: got {id:?}"
110 );
111
112 // 4. `build_id()` and the `BUILD_ID` const are the SAME string
113 // (callers may use either; they must never diverge — that
114 // divergence IS the §gap-3 bug, just internalised).
115 assert_eq!(build_id(), BUILD_ID);
116 }
117
118 #[test]
119 fn proto_and_cas_are_reexported() {
120 let _h = InputHash::new("x");
121 let _s = LocalDiskStore::new(std::env::temp_dir());
122 let _e = StateEvent::BecameRed;
123 let _f = FileState::Green;
124 let identity = BuildIdentity {
125 source_tree: ContentHash::new("a"),
126 cargo_lock: ContentHash::new("b"),
127 rust_toolchain: ContentHash::new("c"),
128 tf_config: ContentHash::new("d"),
129 target: TargetTriple::new("wasm32-unknown-unknown"),
130 profile: Profile::Dev,
131 };
132 let _m = ArtifactMeta {
133 input_hash: InputHash::new("x"),
134 identity,
135 };
136 }
137}