1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
//! Central registry of schema versions across the workspace.
//!
//! Why this exists: before this module, the four schema-version constants
//! lived independently in their owning crates. They all happened to be
//! `"1.0.0"`, which masked a real bug — a comparison that should have
//! checked compatibility between two of them silently passed because the
//! values coincided. (Fixed in commit `68fcb7c`.)
//!
//! Going forward, every schema-version constant in the workspace is
//! sourced from this registry. The pinned-snapshot test in this module
//! is the gate: bumping a version requires intentionally updating the
//! snapshot table, which surfaces the change in code review.
//!
//! # Components covered
//!
//! | `SchemaComponent` variant | Owning surface |
//! |--------------------------------|---------------------------------------------|
//! | `DoctorReliability` | `rch doctor --reliability` JSON envelope |
//! | `Status` | `rch status --json` envelope |
//! | `RepoUpdaterContract` | repo-updater protocol over the wire |
//! | `ProcessTriageContract` | process-triage protocol over the wire |
//!
//! # Bump policy
//!
//! See bead `remote_compilation_helper-62u24.11` "Schema-version bump
//! policy + migration playbook" for the canonical procedure. In short:
//! - MAJOR: removal or rename (breaking)
//! - MINOR: new field or new enum variant (additive)
//! - PATCH: serialization-order change or doc-only change affecting
//! golden tests
//!
//! When you bump, ALSO update [`tests::test_schema_versions_match_snapshot`].
//! Reviewers see the snapshot delta and confirm the rationale.
use serde::{Deserialize, Serialize};
/// Stable identifier for each component that exposes a versioned schema.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SchemaComponent {
/// `rch doctor --reliability` response envelope.
DoctorReliability,
/// `rch status --json` response envelope.
Status,
/// Repo-updater wire contract.
RepoUpdaterContract,
/// Process-triage wire contract.
ProcessTriageContract,
/// Incident event schema (shared remediation reason-code ledger).
IncidentLedger,
/// Worker capability fact schema (exact host/user/path inventory).
WorkerFacts,
/// Durable proof-intent record schema (deferred proof queue).
ProofIntent,
/// Durable proof-replay state record schema (deferred proof conveyor).
ProofReplayState,
/// Worker temporary-bypass record schema (transient-quarantine lifecycle).
WorkerBypassRecord,
/// Shared secret-redaction / privacy policy for all remediation data.
RedactionPolicy,
/// Operator-facing remediation view rendered by the TUI/web dashboards.
RemediationView,
/// Canonical placement / visibility / strict-remote control plan
/// (`rch diagnose` placement surface + `rch exec` control state).
PlacementPlan,
/// Fleet binary deploy audit record: release provenance, verification
/// status, and rollback trail (`rch fleet deploy` audit log).
FleetDeployAudit,
}
/// The canonical version string for a given component.
///
/// `const fn` so callers can use the result in `pub const` declarations
/// (preserves the existing call sites that read into compile-time
/// constants).
#[must_use]
pub const fn current_version(component: SchemaComponent) -> &'static str {
match component {
SchemaComponent::DoctorReliability => "1.0.0",
SchemaComponent::Status => "1.0.0",
SchemaComponent::RepoUpdaterContract => "1.0.0",
SchemaComponent::ProcessTriageContract => "1.0.0",
SchemaComponent::IncidentLedger => "1.0.0",
SchemaComponent::WorkerFacts => "1.0.0",
SchemaComponent::ProofIntent => "1.0.0",
SchemaComponent::ProofReplayState => "1.0.0",
SchemaComponent::WorkerBypassRecord => "1.0.0",
SchemaComponent::RedactionPolicy => "1.0.0",
SchemaComponent::RemediationView => "1.0.0",
SchemaComponent::PlacementPlan => "1.0.0",
SchemaComponent::FleetDeployAudit => "1.0.0",
}
}
/// Every component, paired with its current version. Iterable in tests
/// and at runtime.
pub const ALL_COMPONENTS: &[(SchemaComponent, &str)] = &[
(
SchemaComponent::DoctorReliability,
current_version(SchemaComponent::DoctorReliability),
),
(
SchemaComponent::Status,
current_version(SchemaComponent::Status),
),
(
SchemaComponent::RepoUpdaterContract,
current_version(SchemaComponent::RepoUpdaterContract),
),
(
SchemaComponent::ProcessTriageContract,
current_version(SchemaComponent::ProcessTriageContract),
),
(
SchemaComponent::IncidentLedger,
current_version(SchemaComponent::IncidentLedger),
),
(
SchemaComponent::WorkerFacts,
current_version(SchemaComponent::WorkerFacts),
),
(
SchemaComponent::ProofIntent,
current_version(SchemaComponent::ProofIntent),
),
(
SchemaComponent::ProofReplayState,
current_version(SchemaComponent::ProofReplayState),
),
(
SchemaComponent::WorkerBypassRecord,
current_version(SchemaComponent::WorkerBypassRecord),
),
(
SchemaComponent::RedactionPolicy,
current_version(SchemaComponent::RedactionPolicy),
),
(
SchemaComponent::RemediationView,
current_version(SchemaComponent::RemediationView),
),
(
SchemaComponent::PlacementPlan,
current_version(SchemaComponent::PlacementPlan),
),
(
SchemaComponent::FleetDeployAudit,
current_version(SchemaComponent::FleetDeployAudit),
),
];
#[cfg(test)]
mod tests {
use super::*;
/// Pinned snapshot of every component's version. Bumping a version
/// in [`current_version`] without updating this table fails the test —
/// the failure is the code-review trigger.
///
/// Order matters: tests assert this list matches [`ALL_COMPONENTS`] in
/// both content AND order, so a refactor that reshuffles the constant
/// surfaces immediately.
const PINNED_SNAPSHOT: &[(SchemaComponent, &str)] = &[
(SchemaComponent::DoctorReliability, "1.0.0"),
(SchemaComponent::Status, "1.0.0"),
(SchemaComponent::RepoUpdaterContract, "1.0.0"),
(SchemaComponent::ProcessTriageContract, "1.0.0"),
(SchemaComponent::IncidentLedger, "1.0.0"),
(SchemaComponent::WorkerFacts, "1.0.0"),
(SchemaComponent::ProofIntent, "1.0.0"),
(SchemaComponent::ProofReplayState, "1.0.0"),
(SchemaComponent::WorkerBypassRecord, "1.0.0"),
(SchemaComponent::RedactionPolicy, "1.0.0"),
(SchemaComponent::RemediationView, "1.0.0"),
(SchemaComponent::PlacementPlan, "1.0.0"),
(SchemaComponent::FleetDeployAudit, "1.0.0"),
];
#[test]
fn test_schema_versions_match_snapshot() {
assert_eq!(
ALL_COMPONENTS.len(),
PINNED_SNAPSHOT.len(),
"Number of components changed without snapshot update. \
Either add the missing entry to PINNED_SNAPSHOT or remove the unused entry from ALL_COMPONENTS."
);
for (i, ((c1, v1), (c2, v2))) in ALL_COMPONENTS
.iter()
.zip(PINNED_SNAPSHOT.iter())
.enumerate()
{
assert_eq!(
(c1, *v1),
(c2, *v2),
"Mismatch at position {i}: ALL_COMPONENTS has ({c1:?}, {v1}); \
PINNED_SNAPSHOT has ({c2:?}, {v2}). \
Bump procedure: edit current_version() AND PINNED_SNAPSHOT in the same commit."
);
}
}
#[test]
fn test_current_version_is_const_eval_friendly() {
// Call current_version() in a const-eval context. The compiler will
// refuse if it ever stops being a `const fn`.
const _DR: &str = current_version(SchemaComponent::DoctorReliability);
const _ST: &str = current_version(SchemaComponent::Status);
const _RU: &str = current_version(SchemaComponent::RepoUpdaterContract);
const _PT: &str = current_version(SchemaComponent::ProcessTriageContract);
}
#[test]
fn test_versions_match_semver_shape() {
for &(_, version) in ALL_COMPONENTS {
let parts: Vec<&str> = version.split('.').collect();
assert_eq!(
parts.len(),
3,
"version {version} must be MAJOR.MINOR.PATCH (got {} parts)",
parts.len()
);
for p in parts {
assert!(
p.chars().all(|ch| ch.is_ascii_digit()),
"version {version} contains non-numeric component {p}"
);
}
}
}
#[test]
fn test_all_components_unique() {
use std::collections::HashSet;
let set: HashSet<_> = ALL_COMPONENTS.iter().map(|(c, _)| *c).collect();
assert_eq!(
set.len(),
ALL_COMPONENTS.len(),
"duplicate SchemaComponent in ALL_COMPONENTS"
);
}
}