fix(worker): reconcile invalid Drive completions and default pages

Accept the Backend shared default page maxima (list 200/search 128), not an unsent 100 limit. Classify semantically invalid completions before receipt reconciliation; query the original identity once and accept only validated committed results, never resend or upgrade revisions.

Regressions fail on 61afdb77 and pass after the fix: real API 201-entry default Tools/native WIP pagination; mismatched completion ID/invalid entry metadata recovery and unresolved/invalid receipt matrices. Root cargo check; Worker 920 unit +110 integration; Server 709 lib +11 binary; Drive unit18/API18; fmt and diff checks passed.
This commit is contained in:
2026-10-09 15:36:06 +09:00
parent 61afdb7773
commit e94af40dab
4 changed files with 251 additions and 21 deletions
@@ -726,6 +726,80 @@ async fn unsafe_names_and_oversized_text_cannot_publish_and_reads_truncate_at_ut
}
}
#[tokio::test]
async fn omitted_discovery_limits_use_backend_maximum_pages_in_tools_and_native_wip() {
let f = Fixture::new(["read_write", "read_only"]).await;
let mut expected = std::collections::BTreeSet::new();
for index in 0..201 {
let created = f
.create(&format!("default-page-{index:03}.md"), "bounded")
.await;
expected.insert(entry(&created)["node_id"].as_str().unwrap().to_owned());
}
let native = native_tools(&f.features[1]);
let root = value(
native_tool(&native, "Inspect", json!({"path":"/drive","refresh":true}))
.await
.unwrap(),
);
let reference = root["interfaces"][0]["reference"].clone();
for use_native in [false, true] {
for (name, operation, mut args, maximum) in [
(
"DriveList",
"list",
json!({}),
server_api::DRIVE_PAGE_MAX_LIMIT as usize,
),
(
"DriveSearch",
"search",
json!({"query":"default-page-"}),
server_api::DRIVE_SEARCH_MAX_LIMIT as usize,
),
] {
let mut actual = Vec::new();
for page_index in 0..2 {
let output = if use_native {
native_tool(&native, "Invoke", json!({"path":"/drive","interface":reference,"operation":operation,"arguments":args})).await
} else {
tool(&f.features[1], name, args.clone()).await
};
let page = value(output.unwrap());
let entries = page["entries"].as_array().unwrap();
assert_eq!(
entries.len(),
if page_index == 0 {
maximum
} else {
201 - maximum
},
"native={use_native} operation={operation}"
);
actual.extend(entries.iter().map(|e| {
e["metadata"]["entry"]["node_id"]
.as_str()
.unwrap()
.to_owned()
}));
if page_index == 0 {
assert!(page["next_after"].is_string());
args["after"] = page["next_after"].clone();
} else {
assert!(page["next_after"].is_null());
}
}
assert_eq!(actual.len(), expected.len());
assert_eq!(
actual
.into_iter()
.collect::<std::collections::BTreeSet<_>>(),
expected
);
}
}
}
#[tokio::test]
async fn list_and_search_pages_are_bounded_disjoint_and_cursors_follow_backend_scope() {
let f = Fixture::new(["read_write", "read_only"]).await;