Skip to main content

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}