1use std::collections::BTreeMap;
2
3pub const MCP_MANIFEST_SCHEMA_VERSION: u32 = 1;
5pub const CONTEXT_PROOF_V1_SCHEMA_VERSION: u32 = 1;
6pub const CONTEXT_IR_V1_SCHEMA_VERSION: u32 = 1;
7pub const INTENT_ROUTE_V1_SCHEMA_VERSION: u32 = 1;
8pub const DEGRADATION_POLICY_V1_SCHEMA_VERSION: u32 = 1;
9pub const WORKFLOW_EVIDENCE_LEDGER_V1_SCHEMA_VERSION: u32 = 1;
10pub const AUTONOMY_DRIVERS_V1_SCHEMA_VERSION: u32 = 1;
11pub const TOKENIZER_TRANSLATION_DRIVER_V1_SCHEMA_VERSION: u32 = 1;
12pub const ATTENTION_LAYOUT_DRIVER_V1_SCHEMA_VERSION: u32 = 1;
13pub const VERIFICATION_OBSERVABILITY_V1_SCHEMA_VERSION: u32 = 1;
14pub const HANDOFF_LEDGER_V1_SCHEMA_VERSION: u32 = 1;
15pub const HANDOFF_TRANSFER_BUNDLE_V1_SCHEMA_VERSION: u32 = 1;
16pub const CCP_SESSION_BUNDLE_V1_SCHEMA_VERSION: u32 = 1;
17pub const KNOWLEDGE_POLICY_V1_SCHEMA_VERSION: u32 = 1;
18pub const GRAPH_REPRODUCIBILITY_V1_SCHEMA_VERSION: u32 = 1;
19pub const A2A_SNAPSHOT_V1_SCHEMA_VERSION: u32 = 1;
20pub const MEMORY_BOUNDARY_V1_SCHEMA_VERSION: u32 = 1;
21pub const GOTCHAS_REMINDERS_V1_SCHEMA_VERSION: u32 = 1;
22pub const PROVIDER_FRAMEWORK_V1_SCHEMA_VERSION: u32 = 1;
23pub const CONTEXT_PACKAGE_V1_SCHEMA_VERSION: u32 = 1;
24pub const CONTEXT_PACKAGE_V2_SCHEMA_VERSION: u32 = 2;
25pub const CONTEXT_SNAPSHOT_V1_SCHEMA_VERSION: u32 = 1;
26
27pub const PACKAGE_EXTENSION: &str = "ctxpkg";
28pub const LEGACY_PACKAGE_EXTENSION: &str = "lctxpkg";
29pub const MAX_PACKAGE_FILE_BYTES: u64 = 10 * 1024 * 1024; pub fn is_package_file(path: &std::path::Path) -> bool {
32 path.extension()
33 .and_then(|e| e.to_str())
34 .is_some_and(|ext| ext == PACKAGE_EXTENSION || ext == LEGACY_PACKAGE_EXTENSION)
35}
36
37pub fn default_package_filename(name: &str, version: &str) -> String {
38 format!("{name}-{version}.{PACKAGE_EXTENSION}")
39}
40
41pub const HTTP_MCP_CONTRACT_VERSION: u32 = 1;
43pub const TEAM_SERVER_CONTRACT_VERSION: u32 = 1;
44pub const CAPABILITIES_CONTRACT_VERSION: u32 = 1;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ContractStatus {
58 Frozen,
59 Stable,
60 Experimental,
61}
62
63impl ContractStatus {
64 pub fn as_str(self) -> &'static str {
65 match self {
66 ContractStatus::Frozen => "frozen",
67 ContractStatus::Stable => "stable",
68 ContractStatus::Experimental => "experimental",
69 }
70 }
71}
72
73pub struct ContractDoc {
76 pub id: &'static str,
78 pub doc_file: &'static str,
80 pub version: u32,
81 pub status: ContractStatus,
82}
83
84pub fn contract_docs() -> Vec<ContractDoc> {
89 use ContractStatus::{Experimental, Frozen, Stable};
90 let doc = |id, doc_file, version, status| ContractDoc {
91 id,
92 doc_file,
93 version,
94 status,
95 };
96 vec![
97 doc("http-mcp", "http-mcp-contract-v1.md", 1, Frozen),
99 doc("team-server", "team-server-contract-v1.md", 1, Frozen),
100 doc("context-ir", "context-ir-v1.md", 1, Frozen),
101 doc(
102 "local-free-invariant",
103 "local-free-invariant-v1.md",
104 1,
105 Frozen,
106 ),
107 doc(
108 "oss-plane-separation",
109 "oss-plane-separation-v1.md",
110 1,
111 Frozen,
112 ),
113 doc("billing-plane", "billing-plane-v1.md", 1, Frozen),
114 doc("wasm-abi", "wasm-abi-v1.md", 1, Frozen),
115 doc("capabilities", "capabilities-contract-v1.md", 1, Stable),
120 doc("billing-plane-v2", "billing-plane-v2.md", 2, Stable),
121 doc("billing-plane-v3", "billing-plane-v3.md", 3, Stable),
123 doc("evidence-bundle", "evidence-bundle-v1.md", 1, Stable),
125 doc("team-server-v2", "team-server-contract-v2.md", 2, Stable),
127 doc("a2a", "a2a-contract-v1.md", 1, Stable),
128 doc(
129 "attention-layout-driver",
130 "attention-layout-driver-v1.md",
131 1,
132 Stable,
133 ),
134 doc("autonomy-drivers", "autonomy-drivers-v1.md", 1, Stable),
135 doc("ccp-session-bundle", "ccp-session-bundle-v1.md", 1, Stable),
136 doc("conformance", "conformance-v1.md", 1, Stable),
137 doc("degradation-policy", "degradation-policy-v1.md", 1, Stable),
138 doc("extension-trust", "extension-trust-v1.md", 1, Stable),
139 doc("extractors", "extractors-v1.md", 1, Stable),
140 doc(
141 "gotchas-reminders",
142 "gotchas-reminders-contract-v1.md",
143 1,
144 Stable,
145 ),
146 doc(
147 "graph-reproducibility",
148 "graph-reproducibility-contract-v1.md",
149 1,
150 Stable,
151 ),
152 doc(
153 "handoff-transfer-bundle",
154 "handoff-transfer-bundle-v1.md",
155 1,
156 Stable,
157 ),
158 doc("intent-route", "intent-route-v1.md", 1, Stable),
159 doc(
160 "knowledge-policy",
161 "knowledge-policy-contract-v1.md",
162 1,
163 Stable,
164 ),
165 doc(
166 "memory-boundary",
167 "memory-boundary-contract-v1.md",
168 1,
169 Stable,
170 ),
171 doc("persona-spec", "persona-spec-v1.md", 1, Stable),
172 doc(
173 "provider-framework",
174 "provider-framework-contract-v1.md",
175 1,
176 Stable,
177 ),
178 doc(
179 "tokenizer-translation-driver",
180 "tokenizer-translation-driver-v1.md",
181 1,
182 Stable,
183 ),
184 doc(
185 "workflow-evidence-ledger",
186 "workflow-evidence-ledger-v1.md",
187 1,
188 Stable,
189 ),
190 doc("wrapped-permalink", "wrapped-permalink-v1.md", 1, Stable),
191 doc("addon-manifest", "addon-manifest-v1.md", 1, Stable),
194 doc(
196 "hosted-personal-index",
197 "hosted-personal-index-v1.md",
198 1,
199 Experimental,
200 ),
201 doc(
202 "personal-cloud-encryption",
203 "personal-cloud-encryption-v1.md",
204 1,
205 Experimental,
206 ),
207 doc(
210 "context-policy-packs",
211 "context-policy-packs-v1.md",
212 1,
213 Experimental,
214 ),
215 doc("device-overview", "device-overview-v1.md", 1, Experimental),
216 doc("email-digest", "email-digest-v1.md", 1, Experimental),
217 doc("org-audit-log", "org-audit-log-v1.md", 1, Experimental),
218 doc("org-sso-oidc", "org-sso-oidc-v1.md", 1, Experimental),
219 doc("quality-loop", "quality-loop-v1.md", 1, Experimental),
221 doc("ctxpkg-registry", "ctxpkg-registry-v1.md", 1, Experimental),
223 doc(
226 "context-snapshot",
227 "context-snapshot-v1.md",
228 1,
229 Experimental,
230 ),
231 doc(
232 "team-invite-links",
233 "team-invite-links-v1.md",
234 1,
235 Experimental,
236 ),
237 doc("org-policy", "org-policy-v1.md", 1, Experimental),
242 doc(
243 "compliance-report",
244 "compliance-report-v1.md",
245 1,
246 Experimental,
247 ),
248 ]
249}
250
251pub fn status_kv() -> BTreeMap<&'static str, &'static str> {
254 contract_docs()
255 .into_iter()
256 .map(|d| (d.id, d.status.as_str()))
257 .collect()
258}
259
260pub fn versions_kv() -> BTreeMap<&'static str, u32> {
261 BTreeMap::from([
262 (
263 "leanctx.contract.mcp_manifest.schema_version",
264 MCP_MANIFEST_SCHEMA_VERSION,
265 ),
266 (
267 "leanctx.contract.context_proof_v1.schema_version",
268 CONTEXT_PROOF_V1_SCHEMA_VERSION,
269 ),
270 (
271 "leanctx.contract.context_ir_v1.schema_version",
272 CONTEXT_IR_V1_SCHEMA_VERSION,
273 ),
274 (
275 "leanctx.contract.intent_route_v1.schema_version",
276 INTENT_ROUTE_V1_SCHEMA_VERSION,
277 ),
278 (
279 "leanctx.contract.degradation_policy_v1.schema_version",
280 DEGRADATION_POLICY_V1_SCHEMA_VERSION,
281 ),
282 (
283 "leanctx.contract.workflow_evidence_ledger_v1.schema_version",
284 WORKFLOW_EVIDENCE_LEDGER_V1_SCHEMA_VERSION,
285 ),
286 (
287 "leanctx.contract.autonomy_drivers_v1.schema_version",
288 AUTONOMY_DRIVERS_V1_SCHEMA_VERSION,
289 ),
290 (
291 "leanctx.contract.tokenizer_translation_driver_v1.schema_version",
292 TOKENIZER_TRANSLATION_DRIVER_V1_SCHEMA_VERSION,
293 ),
294 (
295 "leanctx.contract.attention_layout_driver_v1.schema_version",
296 ATTENTION_LAYOUT_DRIVER_V1_SCHEMA_VERSION,
297 ),
298 (
299 "leanctx.contract.verification_observability_v1.schema_version",
300 VERIFICATION_OBSERVABILITY_V1_SCHEMA_VERSION,
301 ),
302 (
303 "leanctx.contract.handoff_ledger_v1.schema_version",
304 HANDOFF_LEDGER_V1_SCHEMA_VERSION,
305 ),
306 (
307 "leanctx.contract.handoff_transfer_bundle_v1.schema_version",
308 HANDOFF_TRANSFER_BUNDLE_V1_SCHEMA_VERSION,
309 ),
310 (
311 "leanctx.contract.ccp_session_bundle_v1.schema_version",
312 CCP_SESSION_BUNDLE_V1_SCHEMA_VERSION,
313 ),
314 (
315 "leanctx.contract.knowledge_policy_v1.schema_version",
316 KNOWLEDGE_POLICY_V1_SCHEMA_VERSION,
317 ),
318 (
319 "leanctx.contract.graph_reproducibility_v1.schema_version",
320 GRAPH_REPRODUCIBILITY_V1_SCHEMA_VERSION,
321 ),
322 (
323 "leanctx.contract.a2a_snapshot_v1.schema_version",
324 A2A_SNAPSHOT_V1_SCHEMA_VERSION,
325 ),
326 (
327 "leanctx.contract.memory_boundary_v1.schema_version",
328 MEMORY_BOUNDARY_V1_SCHEMA_VERSION,
329 ),
330 (
331 "leanctx.contract.gotchas_reminders_v1.schema_version",
332 GOTCHAS_REMINDERS_V1_SCHEMA_VERSION,
333 ),
334 (
335 "leanctx.contract.provider_framework_v1.schema_version",
336 PROVIDER_FRAMEWORK_V1_SCHEMA_VERSION,
337 ),
338 (
339 "leanctx.contract.context_package_v1.schema_version",
340 CONTEXT_PACKAGE_V1_SCHEMA_VERSION,
341 ),
342 (
343 "leanctx.contract.context_package_v2.schema_version",
344 CONTEXT_PACKAGE_V2_SCHEMA_VERSION,
345 ),
346 (
347 "leanctx.contract.context_snapshot_v1.schema_version",
348 CONTEXT_SNAPSHOT_V1_SCHEMA_VERSION,
349 ),
350 (
351 "leanctx.contract.http_mcp.contract_version",
352 HTTP_MCP_CONTRACT_VERSION,
353 ),
354 (
355 "leanctx.contract.team_server.contract_version",
356 TEAM_SERVER_CONTRACT_VERSION,
357 ),
358 (
359 "leanctx.contract.capabilities.contract_version",
360 CAPABILITIES_CONTRACT_VERSION,
361 ),
362 ])
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn contract_docs_have_unique_ids_and_files() {
371 let docs = contract_docs();
372 let mut ids: Vec<_> = docs.iter().map(|d| d.id).collect();
373 let mut files: Vec<_> = docs.iter().map(|d| d.doc_file).collect();
374 ids.sort_unstable();
375 files.sort_unstable();
376 let unique_ids: std::collections::BTreeSet<_> = ids.iter().collect();
377 let unique_files: std::collections::BTreeSet<_> = files.iter().collect();
378 assert_eq!(unique_ids.len(), docs.len(), "duplicate contract id");
379 assert_eq!(unique_files.len(), docs.len(), "duplicate doc file");
380 }
381
382 #[test]
383 fn frozen_set_covers_the_platform_promises() {
384 let docs = contract_docs();
388 for id in [
389 "http-mcp",
390 "team-server",
391 "context-ir",
392 "local-free-invariant",
393 "oss-plane-separation",
394 "billing-plane",
395 "wasm-abi",
396 ] {
397 let entry = docs.iter().find(|d| d.id == id).expect("listed");
398 assert_eq!(
399 entry.status,
400 ContractStatus::Frozen,
401 "{id} must stay frozen"
402 );
403 }
404 }
405
406 #[test]
407 fn status_kv_matches_docs() {
408 let kv = status_kv();
409 assert_eq!(kv.len(), contract_docs().len());
410 assert_eq!(kv["http-mcp"], "frozen");
411 assert_eq!(kv["hosted-personal-index"], "experimental");
412 assert_eq!(kv["personal-cloud-encryption"], "experimental");
413 }
414
415 #[test]
416 fn doc_files_follow_versioned_naming() {
417 for d in contract_docs() {
420 assert!(
421 d.doc_file.ends_with(&format!("-v{}.md", d.version)),
422 "{} must end in -v{}.md",
423 d.doc_file,
424 d.version
425 );
426 }
427 }
428}