///|
fn ReviewBundle::require_same_inputs(
self : ReviewBundle,
other : ReviewBundle,
) -> Unit raise MergeError {
if self.base != other.base ||
self.ours != other.ours ||
self.theirs != other.theirs ||
self.keys != other.keys ||
self.ordering != other.ordering ||
self.lineage != other.lineage ||
self.rows != other.rows ||
self.evolution != other.evolution {
raise InvalidReview(
"review snapshots, keys or policies differ; regenerate reviews from the same baseline",
)
}
}
///|
/// Combines independently reviewed copies of this baseline without overwrites.
/// Baseline choices must be retained exactly; agreeing additions coalesce.
/// Conflicting choices or invalid structural dependencies reject the whole batch.
pub fn ReviewBundle::combine(
self : ReviewBundle,
reviews : Array[ReviewBundle],
) -> ReviewBundle raise MergeError {
if reviews.length() > 256 {
raise LimitExceeded("combine accepts at most 256 review bundles per batch")
}
let _ = self.plan()
let inherited : Map[String, Choice] = Map([])
let additions : Map[String, Decision] = Map([])
for decision in self.choices {
inherited[decision.target.to_json().stringify()] = decision.choice
}
for review in reviews {
self.require_same_inputs(review)
let plan = review.plan()
let supplied : Map[String, Choice] = Map([])
for decision in plan.decisions_data {
supplied[decision.target.to_json().stringify()] = decision.choice
}
for token, choice in inherited {
if supplied.get(token) != Some(choice) {
raise InvalidReview(
"review changed or revoked baseline decision at \{token}; revise the baseline explicitly and restart assignments",
)
}
}
for decision in plan.decisions_data {
let token = decision.target.to_json().stringify()
if inherited.contains(token) {
continue
}
match additions.get(token) {
Some(existing) if existing.choice != decision.choice =>
raise InvalidReview(
"reviewers disagree at \{token}; choose explicitly in a new shared baseline",
)
Some(_) => ()
None => {
if inherited.length() + additions.length() >= max_cells {
raise LimitExceeded("combined review exceeds 2000000 decisions")
}
additions[token] = decision
}
}
}
}
let ordered = additions.to_array()
ordered.sort_by((a, b) => lexical_string(a.0, b.0))
let decisions = self.choices.copy()
for (_, decision) in ordered {
decisions.push(decision)
}
let combined = calculate(
self.base,
self.ours,
self.theirs,
self.keys,
decisions,
self.ordering,
lineage=self.lineage,
rows=self.rows,
evolution=self.evolution,
) catch {
InvalidDecision(reason) =>
raise InvalidReview(
reason +
"; combined review rejected: reconcile structural dependencies explicitly",
)
error => raise error
}
ReviewBundle::from_plan(combined)
}