Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions tray/src/web_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12190,6 +12190,19 @@ fn normalize_shared_binding_value(binding: &mut serde_json::Value) {
});
obj.entry("sync_layout".to_string())
.or_insert_with(|| serde_json::Value::String("legacy_bucket_root".to_string()));
// The UI contract types pods_provisioned as a required array; route-only
// sidecars (schema v3 progressive bindings) may omit it entirely, and a
// hand-edited sidecar could carry null or a non-array.
let pods_is_array = obj
.get("pods_provisioned")
.map(|v| v.is_array())
.unwrap_or(false);
if !pods_is_array {
obj.insert(
"pods_provisioned".to_string(),
serde_json::Value::Array(Vec::new()),
);
}

let pods = obj
.get("pods_provisioned")
Expand Down Expand Up @@ -19092,6 +19105,55 @@ mod tests {
assert!(claus.contains("HANDOFF.md"));
}

#[test]
fn normalize_backfills_pods_provisioned_for_route_only_sidecar() {
// Regression: a schema-v3 progressive-sync sidecar carries only
// routes_provisioned; serving it without pods_provisioned crashed the
// Files and Settings sharing UIs (2026-07-08).
let mut binding = serde_json::json!({
"schema_version": 3,
"bucket": "tytus-progressive-canary",
"slug": "p6canary",
"local_path": "/tmp/p6canary/",
"auto_sync": false,
"routes_provisioned": ["r0g3", "r1adv"],
});
normalize_shared_binding_value(&mut binding);
assert_eq!(
binding.get("pods_provisioned"),
Some(&serde_json::json!([])),
"route-only sidecars must serialize pods_provisioned as an empty array"
);
// Malformed values (null / non-array) are coerced, not passed through.
let mut with_null = serde_json::json!({
"schema_version": 2,
"bucket": "shared",
"slug": "shared",
"local_path": "/tmp/shared/",
"auto_sync": true,
"pods_provisioned": serde_json::Value::Null,
});
normalize_shared_binding_value(&mut with_null);
assert_eq!(
with_null.get("pods_provisioned"),
Some(&serde_json::json!([]))
);
// Existing pod lists must pass through untouched.
let mut with_pods = serde_json::json!({
"schema_version": 2,
"bucket": "shared",
"slug": "shared",
"local_path": "/tmp/shared/",
"auto_sync": true,
"pods_provisioned": ["wannolot-01"],
});
normalize_shared_binding_value(&mut with_pods);
assert_eq!(
with_pods.get("pods_provisioned"),
Some(&serde_json::json!(["wannolot-01"]))
);
}

#[test]
fn shared_folder_direct_answer_fails_closed_when_selected_but_grant_missing() {
let _grant_guard = shared_grant_test_serial_lock();
Expand Down
Loading