From c1b3b95c5da9f1e96ce4204adf055be791d93292 Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 9 Oct 2026 01:13:15 +0900 Subject: [PATCH] test: wip validate scope replacement and target compatibility --- crates/worker/src/wip/ancestor_tests.rs | 81 +++++++++++++++++++ .../development/wip-integration-validation.md | 26 +++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/crates/worker/src/wip/ancestor_tests.rs b/crates/worker/src/wip/ancestor_tests.rs index b7f01786f..ab1ac929a 100644 --- a/crates/worker/src/wip/ancestor_tests.rs +++ b/crates/worker/src/wip/ancestor_tests.rs @@ -457,3 +457,84 @@ async fn ancestor_replacement_during_in_flight_call_keeps_the_original_descripto assert_eq!(new.content.as_deref(), Some("2")); assert_eq!(provider.calls.load(Ordering::SeqCst), 2); } + +#[tokio::test] +async fn same_name_scope_replacement_or_loss_of_ref_rejects_old_metadata_without_replay() { + for identity in [Some("replacement"), None] { + let (runtime, provider) = fixture("/github/team", true, false); + let selected = reference("/github/team", "read"); + runtime + .inspect(provider.target_path.clone(), false) + .await + .unwrap(); + { + let mut state = provider.state.lock().unwrap(); + state.identity = identity.map(Into::into); + if identity.is_some() { + state.generation = 1; + } + } + assert_eq!( + rejected_code( + &runtime, + metadata_request( + &provider.target_path, + json!({"scope":"/github/team","name":"read"}), + None, + Some("live-scope") + ) + ) + .await, + "interface_mismatch" + ); + let failed = runtime + .invoke( + provider.target_path.clone(), + selected.clone(), + "read".into(), + json!({}), + ToolExecutionContext::direct(), + ) + .await + .unwrap_err(); + assert!(failed.to_string().contains("InterfaceMismatch"), "{failed}"); + assert_eq!(provider.calls.load(Ordering::SeqCst), 0); + let fetched = runtime.host.fetch_interface_live(&selected).await.unwrap(); + assert_eq!(fetched.scope_ref.as_deref(), identity); + assert_eq!( + fetched.descriptor.documentation.unwrap().summary, + if identity.is_some() { + "Published generation 1" + } else { + "Published generation 0" + } + ); + runtime + .inspect(provider.target_path.clone(), true) + .await + .unwrap(); + runtime + .invoke( + provider.target_path.clone(), + selected, + "read".into(), + json!({}), + ToolExecutionContext::direct(), + ) + .await + .unwrap(); + assert_eq!(provider.calls.load(Ordering::SeqCst), 1); + let state = runtime.state.lock().unwrap(); + let call = state + .client + .call_history(&state.session) + .unwrap() + .back() + .unwrap(); + // Omitted scope_ref is never filled from the placeholder or other observations. + assert_eq!( + call.context.request().interface.scope_ref.as_deref(), + identity + ); + } +} diff --git a/docs/development/wip-integration-validation.md b/docs/development/wip-integration-validation.md index 5a549120e..577ca28e7 100644 --- a/docs/development/wip-integration-validation.md +++ b/docs/development/wip-integration-validation.md @@ -17,10 +17,10 @@ Cargo resolves the release, not that checkout or an unreleased Rust revision. ## Executed checks on registry 0.2.0 -- `cargo test -p worker --locked --offline`: **893 unit + 110 integration tests +- `cargo test -p worker --locked --offline`: **894 unit + 110 integration tests passed**, no ignored/failed tests. This includes ordinary Tools mode, Feature permission/revocation, restore, session/history and controller integration. -- Narrow development checks: `wip::binding` (25), `wip::provider_tests` (11), existing +- Narrow development checks: `wip::binding` (25), `wip::provider_tests` (12), existing `wip::tests` and workdir projection tests (43), checkout transports/providers (20), workspace-config production/authority tests (19). These are included in the complete worker run. @@ -36,6 +36,26 @@ Cargo resolves the release, not that checkout or an unreleased Rust revision. hash. This validates credential-free dependency acquisition, not a full Nix binary/image build or deployment update. +## Current target compatibility (preview, not integration) + +Source runtime at `23cf6c4f882ce78985ba4bd673751cebe64e2d87` and current develop +`b5ea162f838fc36e364edec9ab6689292b59cffd` produce the conflict-free `git merge-tree` +preview tree `7ef3545b635cbdfc5e24658f6d1532e0f8b0947c`. An archive of this tree, +without a branch/ref change, passed root locked/offline Cargo check, all **91** +Server API TypeScript tests (including OpenAPI/checked-in generator freshness), +registry WIP resolution checks, and Nix dependency acquisition with the same hash. +T-722's API macros, Server API/Drive/Server source, OpenAPI and generated TS blobs +are unchanged from develop in the combined tree; no artifact overwrite or +regeneration is necessary for this source change. The later added same-name scope +regression changes tests only, not the validated runtime or shared contracts. + +The initial archive check reused a Cargo target directory; returning to the older +main checkout exposed stale preview Server API artifacts. All local workspace +artifacts were invalidated, and a fresh main root check and complete **894 + 110** +Worker suite passed. Use an isolated target directory for future cross-tree checks. +No target push, integration branch, deployment, or live dogfood update was made. +Orchestrator readiness/integration must still resolve the then-current target. + ## Contract evidence `crates/worker/src/wip/binding.rs` tests exercise fresh/loading/stale/failed and @@ -60,6 +80,8 @@ valid live ancestor refs that differ from registration placeholders, root `/` an intermediate provider-owned ancestors, ref-less and ref-bearing scope deletion without a prior scope Inspect, rejected cached dispatch and orphan observation, replacement/republication without restoring old Interface registrations, +same-name scope replacement or loss of public ref rejecting copied old metadata +without replay (and later explicit Inspect/Invoke copying only the new optional ref), shape/target/Object-validator/Interface-mismatch precedence, and frozen in-flight ancestor Descriptor/handler/result validation after replacement. Valid Invoke makes one provider publication call for the target, not a separate scope lookup.