///|
/// A proposal after replaying explicit decisions and full inheritance checks.
/// Blocked proposals expose evidence, never a partial candidate or bundle.
pub struct ReconciledMerge {
priv original : MergeReport
priv resolution : MergeResolution
priv validated : MergeReport
}
///|
pub fn MergeReport::reconcile(
self : MergeReport,
resolution : MergeResolution,
) -> ReconciledMerge raise TraceError {
for
(name, expected, actual) in [
("base", self.base, resolution.base),
("left", self.left, resolution.left),
("right", self.right, resolution.right),
] {
if expected.to_manifest_json() != actual.to_manifest_json() {
reject(
"stale_merge_resolution",
"/" + name,
"Resolution is bound to different input content; regenerate the template and review the changed evidence.",
)
}
}
{
original: self,
resolution,
validated: self.base.merge_with(self.left, self.right, Some(resolution)),
}
}
///|
pub fn ReconciledMerge::candidate(self : ReconciledMerge) -> DatasetIndex? {
self.validated.candidate()
}
///|
pub fn ReconciledMerge::conflicts(
self : ReconciledMerge,
) -> Array[MergeConflict] {
self.validated.conflicts()
}
///|
pub fn ReconciledMerge::conflict_count(self : ReconciledMerge) -> Int32 {
self.validated.conflict_count()
}
///|
pub fn ReconciledMerge::impact(self : ReconciledMerge) -> ImpactReport? {
self.validated.impact()
}
///|
/// Original conflicts, all decisions/reasons, all three inputs and the complete
/// revalidated base-to-candidate evidence. This is not a release approval.
pub fn ReconciledMerge::to_json(self : ReconciledMerge) -> String {
"{\"initial\":" +
self.original.to_json() +
",\"resolution\":" +
self.resolution.to_json() +
",\"revalidated\":" +
self.validated.to_json() +
"}"
}
///|
/// Self-contained reconciliation and release evidence. Replay checks agreement,
/// not authorship, signatures, dataset authenticity or release authorization.
pub struct MergeReviewBundle {
priv reconciliation : ReconciledMerge
priv release : ReviewBundle
priv serialized : String
}
///|
pub fn ReconciledMerge::review_bundle(
self : ReconciledMerge,
policy : ReleasePolicy,
) -> MergeReviewBundle raise TraceError {
guard self.candidate() is Some(after) else {
reject(
"reconciliation_blocked", "", "Unresolved conflicts block bundle creation; inspect revalidated conflicts and revise the explicit decisions.",
)
abort("reject raises")
}
let release = ReviewBundle::create(self.original.base, after, policy)
let parts = [
"{\"expected\":",
self.to_json(),
",\"format\":\"bids-metadata-trace/merge-review\",\"release\":",
release.to_json(),
",\"resolution\":",
self.resolution.to_json(),
",\"version\":1}",
]
let mut length = 0
for part in parts {
if part.length() > 16777216 - length {
reject(
"bundle_limit", "", "Complete merge review exceeds 16,777,216 UTF-16 units; narrow the snapshots or decisions.",
)
}
length += part.length()
}
{ reconciliation: self, release, serialized: parts.join(""), }
}
///|
/// Recompute both the initial merge and reconciled candidate, verify all
/// recorded evidence and the release bundle's exact snapshots and policy.
pub fn MergeReviewBundle::from_json(
text : String,
) -> MergeReviewBundle raise TraceError {
let fields = resolution_object(
parse_source(
"",
text,
container_limit=96,
text_limit=16777216,
),
["expected", "format", "release", "resolution", "version"],
"",
)
if fields["format"] != Json::string("bids-metadata-trace/merge-review") ||
canonical_json(fields["version"]) != "1" {
reject(
"unsupported_merge_review", "", "Expected bids-metadata-trace/merge-review version 1.",
)
}
let resolution = MergeResolution::from_json(
canonical_json(fields["resolution"]),
)
let reconciled = resolution.base
.merge(resolution.left, resolution.right)
.reconcile(resolution)
if canonical_json(fields["expected"]) != reconciled.to_json() {
reject(
"replay_mismatch", "/expected", "Initial or revalidated evidence differs from the complete recorded inputs and decisions.",
)
}
let release = ReviewBundle::from_json(canonical_json(fields["release"]))
let regenerated = reconciled.review_bundle(release.report.policy)
if release.to_json() != regenerated.release.to_json() {
reject(
"replay_mismatch", "/release", "Release evidence must use this reconciliation's exact base and candidate snapshots.",
)
}
regenerated
}
///|
pub fn MergeReviewBundle::to_json(self : MergeReviewBundle) -> String {
self.serialized
}
///|
pub fn MergeReviewBundle::reconciliation(
self : MergeReviewBundle,
) -> ReconciledMerge {
self.reconciliation
}
///|
pub fn MergeReviewBundle::report(self : MergeReviewBundle) -> ReleaseCheck {
self.release.report()
}