From 7eea7472700455c185a18ab563ca62db5b4a7bb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 23:13:08 +0000 Subject: [PATCH 1/2] dir-core: keep the fold's sole Entra->EXO row ordinal in Output fold's cloud merge already decides, per Entra row, whether exactly one Exchange Online row names it, then discarded the row. Output now keeps it as entra_mailbox (NO_ROW for none or several), and Ruler::from_fold reads it instead of running a second merge. Consumers binding a hybrid AD->Entra->EXO coordinate no longer need their own Entra->EXO lookup. The ogar-az suite checks the field against a scalar scan on every fixture, including the randomized one, and pins the duplicate and cross-tenant cases. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XAqKMMJzKZrYnckXRC8x2U --- crates/ogar-az/tests/correspond.rs | 63 +++++++++++++++++++++++++- crates/ogar-dir-core/src/correspond.rs | 15 +++--- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/crates/ogar-az/tests/correspond.rs b/crates/ogar-az/tests/correspond.rs index 965e215a..1aa5c67c 100644 --- a/crates/ogar-az/tests/correspond.rs +++ b/crates/ogar-az/tests/correspond.rs @@ -9,8 +9,8 @@ use ogar_ad::AdEntry; use ogar_dir_core::correspond::{ - AdStatus, Binding, Index, Lanes, Output, Planes, Profile, Ruler, Source, flag, fold, - fold_aligned, + AdStatus, Binding, Index, Lanes, NO_ROW, Output, Planes, Profile, Ruler, Source, UNBOUND, flag, + fold, fold_aligned, state, }; use ogar_dir_core::identity::{ BACKSYNC_TO_ENTRA_ID, CONSISTENCY_GUID_TO_IMMUTABLE_ID, ENTRA_ID_TO_EXO_EXTERNAL_ID, @@ -280,6 +280,11 @@ fn run_encoded(e: &Encoded, anchor: IdentityRule, b: &[Binding]) -> Run { let mut out = Output::for_index(&lanes, &ix); fold(&lanes, &ix, &mut out); assert_eq!(out.edges.len(), ix.edges, "the index counts every edge"); + assert_eq!( + out.entra_mailbox, + mailbox_oracle(&lanes), + "the fold's mailbox ordinal is the unique in-scope match" + ); // The same rows on one ruler: the position-wise fold confirms exactly // the slots the join confirms. let ruler = Ruler::from_fold(&lanes, &ix, &out); @@ -297,6 +302,29 @@ fn run_encoded(e: &Encoded, anchor: IdentityRule, b: &[Binding]) -> Run { Run { lanes, out } } +/// Scalar reference for [`Output::entra_mailbox`]: a linear scan, without the +/// index or the merge. Exactly one present, same-scope Exchange Online row +/// naming the Entra id, else `NO_ROW`. +fn mailbox_oracle(l: &Lanes) -> Vec { + (0..l.entra.len()) + .map(|j| { + let scope = l.entra.scope[j]; + if scope == UNBOUND { + return NO_ROW; + } + let mut hits = (0..l.exo.len()).filter(|&x| { + l.exo.scope[x] == scope + && l.exo_external.state[x] == state::PRESENT + && l.exo_external.id[x] == l.entra.owner[j] + }); + match (hits.next(), hits.next()) { + (Some(x), None) => x as u32, + _ => NO_ROW, + } + }) + .collect() +} + fn statuses(r: &Run) -> Vec { r.out.ad.iter().map(|&f| AdStatus::of(f)).collect() } @@ -814,6 +842,7 @@ fn f14_observations_are_not_touched() { ); assert_ne!(r.out.entra[0] & flag::E_EXO_ONE, 0); assert_ne!(r.out.exo[0] & flag::X_ONE, 0); + assert_eq!(r.out.entra_mailbox, [0], "the one mailbox, by row ordinal"); } #[test] @@ -836,12 +865,42 @@ fn exchange_online_dangling_and_duplicate() { }; let r = run(&w, CONSISTENCY_GUID_TO_IMMUTABLE_ID); assert_ne!(r.out.entra[0] & flag::E_EXO_MANY, 0); + assert_eq!(r.out.entra_mailbox, [NO_ROW], "two mailboxes: no choice"); assert_eq!( r.out.exo, [flag::X_ONE, flag::X_ONE, flag::X_DANGLING, flag::X_ABSENT] ); } +/// The mailbox ordinal is scoped: the same external id in another bound +/// tenant names a different Entra row, never this one. +#[test] +fn mailbox_ordinal_does_not_cross_tenants() { + let x = g(3); + let mut there = entra(x, Wire::Absent); + there.tenant = g(OTHER_TENANT); + let exo = |n, tenant| Exo { + guid: g(n), + tenant: g(tenant), + external: Wire::Id(x), + }; + let w = World { + entra: vec![entra(x, Wire::Absent), there], + exo: vec![exo(40, OTHER_TENANT), exo(41, TENANT)], + ..World::default() + }; + let both = [ + bindings()[0], + Binding { + ad_domain: g(OTHER_DOMAIN), + tenant: g(OTHER_TENANT), + }, + ]; + let r = run_encoded(&encode(&w), CONSISTENCY_GUID_TO_IMMUTABLE_ID, &both); + assert_ne!(r.lanes.entra.scope[0], r.lanes.entra.scope[1]); + assert_eq!(r.out.entra_mailbox, [1, 0]); +} + // ---- randomized comparison -------------------------------------------------- struct Rng(u64); diff --git a/crates/ogar-dir-core/src/correspond.rs b/crates/ogar-dir-core/src/correspond.rs index 590c88e8..c4a41e13 100644 --- a/crates/ogar-dir-core/src/correspond.rs +++ b/crates/ogar-dir-core/src/correspond.rs @@ -398,6 +398,9 @@ pub struct Output { pub ad_back: Vec, /// Per Entra row. pub entra: Vec, + /// Per Entra row: the one Exchange Online row whose external id names + /// it, else [`NO_ROW`] (none, or several: a choice is never made). + pub entra_mailbox: Vec, /// Per Entra row: the AD row whose anchor alone claims it, else [`NO_ROW`]. pub entra_claimer: Vec, /// Per Entra row: the AD row whose backsync alone names it, else [`NO_ROW`]. @@ -416,6 +419,7 @@ impl Output { ad_target: vec![NO_ROW; l.ad.len()], ad_back: vec![NO_ROW; l.ad.len()], entra: vec![0; l.entra.len()], + entra_mailbox: vec![NO_ROW; l.entra.len()], entra_claimer: vec![NO_ROW; l.entra.len()], entra_namer: vec![NO_ROW; l.entra.len()], exo: vec![0; l.exo.len()], @@ -585,6 +589,7 @@ pub fn fold(l: &Lanes, ix: &Index, out: &mut Output) { let mbx = count_flag(x.len(), 0, flag::E_EXO_ONE, flag::E_EXO_MANY); for y in e { out.entra[y.row as usize] |= mbx; + out.entra_mailbox[y.row as usize] = sole(x); } }); @@ -747,13 +752,9 @@ impl Ruler { exo_external: vec![Guid128::NIL; n], max_skew_ms: l.max_skew_ms, }; - // The one mailbox of each Entra row, if exactly one. - let mut mailbox = vec![NO_ROW; l.entra.len()]; - merge(&ix.exo_by_external, &ix.entra_by_id, |x, e| { - for y in e { - mailbox[y.row as usize] = sole(x); - } - }); + // The one mailbox of each Entra row comes from the fold; no second + // merge. + let mailbox = &out.entra_mailbox; for i in 0..n { if l.ad_anchor.state[i] == state::PRESENT && out.ad[i] & flag::ANCHOR_SHARED == 0 { r.ad_anchor[i] = l.ad_anchor.id[i]; From 455004b9e9f915bd3882896310f1eea143e490fe Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 23:13:46 +0000 Subject: [PATCH 2/2] dir-core: keep Ruler::from_fold's index parameter, now unused Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XAqKMMJzKZrYnckXRC8x2U --- crates/ogar-dir-core/src/correspond.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ogar-dir-core/src/correspond.rs b/crates/ogar-dir-core/src/correspond.rs index c4a41e13..83b06c9f 100644 --- a/crates/ogar-dir-core/src/correspond.rs +++ b/crates/ogar-dir-core/src/correspond.rs @@ -740,7 +740,10 @@ impl Ruler { /// failing that exactly one backsync target; exactly one mailbox); /// anything else leaves the cell empty, so ambiguity can never be read /// back as agreement. - pub fn from_fold(l: &Lanes, ix: &Index, out: &Output) -> Self { + /// + /// `_ix` is unused since the mailbox ordinal moved into [`Output`]; it + /// stays so callers keep compiling. + pub fn from_fold(l: &Lanes, _ix: &Index, out: &Output) -> Self { let n = l.ad.len(); let mut r = Self { ad_anchor: vec![Guid128::NIL; n],