diff --git a/ci/permutation/changes.mjs b/ci/permutation/changes.mjs index 191b5c8..7e26d71 100644 --- a/ci/permutation/changes.mjs +++ b/ci/permutation/changes.mjs @@ -123,7 +123,9 @@ const tag = (ctx) => `r${ctx.round}${ctx.side === "target" ? "t" : "s"}`; export const CHANGES = { async "content.edit"(ctx) { - const pool = (await listsWithItems(ctx.c, ctx.guid, ctx.locale)).flatMap((x) => x.items); + const pool = (await listsWithItems(ctx.c, ctx.guid, ctx.locale)) + .flatMap((x) => x.items) + .filter((it) => !ctx.avoid?.has(it.contentID)); let full = null; for (const it of [...pool].sort(() => ctx.r.next() - 0.5)) { const candidate = await ctx.c.contentMethods.getContentItem(it.contentID, ctx.guid, ctx.locale); @@ -368,9 +370,11 @@ export const CHANGES = { }, }; -// Changes made on the other instance than the one being synced from, in the same round. -export async function drift(ctx) { - return CHANGES["content.edit"]({ ...ctx }); +// Changes made on the other instance than the one being synced from, in the same round. `avoid` is +// the receiving side's copies of items the round changed on the sending side: an edit to one of those +// is a change on both sides, which the conflict check covers, not drift. +export async function drift(ctx, avoid = new Set()) { + return CHANGES["content.edit"]({ ...ctx, avoid }); } // The same item edited on both sides with different values: a conflict. `avoidDstId` is the item diff --git a/ci/permutation/run.mjs b/ci/permutation/run.mjs index a992b48..e0f1f4e 100644 --- a/ci/permutation/run.mjs +++ b/ci/permutation/run.mjs @@ -251,7 +251,12 @@ for (let i = 0; i < plan.length; i++) { let drifted = null; try { - drifted = await drift(toCtx); + // The receiving side's copies of items this round edited on the sending side + const editedThere = applied + .filter((a) => a.area === "content" && a.change === "edit") + .map((a) => mapped(forward)(Number(/#(\d+)/.exec(a.detail)?.[1]))) + .filter(Boolean); + drifted = await drift(toCtx, new Set(editedThere)); if (drifted) applied.push({ name: "drift", ...drifted, area: "drift" }); } catch (e) { record(round, direction, "apply:drift", false, String(e?.message ?? e).slice(0, 200));