diff --git a/demo/smoke_post_write_adoption.mjs b/demo/smoke_post_write_adoption.mjs index 1cd3f8a9..a322964f 100644 --- a/demo/smoke_post_write_adoption.mjs +++ b/demo/smoke_post_write_adoption.mjs @@ -80,6 +80,13 @@ const out = await page.evaluate(async () => { }; const reset = async (scenario) => { + // Ревью r2 M1: отложенная запись предыдущего сценария иначе доживает до + // следующего и флашится безусловным `if (pending) flush()` внутри + // `_deleteSpace`; мок отвечает `{}` без `rev`, запасной путь «rev + 1» + // двигает ревизию по ЧУЖОМУ телу, и отказная ветка выглядит принявшей. + // Сценарии обязаны быть независимы — так же чистится smoke_danger_confirmation. + card._saveConfigDebounced.cancel(); + card._persistLayout.cancel(); server = { config: freshConfig(), layout: {}, cfgRev: 10, layRev: 20 }; concurrentHref = `media-source://image/${scenario}-500`; adoption.restoreCached({ config: structuredClone(server.config), rev: 10, layout: {}, layout_rev: 20 }); diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index 52baf458..7406b4f4 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -8555,6 +8555,20 @@ const MUTANT_DEFINITIONS = [ + ' || contentFingerprint(current) !== attempt.attemptedFingerprint) return false;', }], }, + { + id: 'post-write-tail-runs-on-refused-gate', + guard: 'node demo/smoke_post_write_adoption.mjs', + because: '#500 r1 M1: a post-write caller whose adoption was refused must skip its tail — ' + + 'clearing history, bumping the geometry epoch and toasting «отменено» on a body the card ' + + 'never adopted leaves the frontend describing state the server does not have; the scheduled ' + + 'reload owns the tail. Guarded by the smoke, so the review gate runs it (r2 M1: the witness ' + + 'was red for a round and nobody saw it — CI never ran this smoke)', + patches: [{ + file: 'src/houseplan-editor-runtime.ts', + find: " if (adopted.status !== 'adopted') return; // asset wait: the scheduled reload owns the tail", + replace: ' void adopted; // mutant: the tail runs on a body that was never adopted', + }], + }, { id: 'post-write-skips-asset-gate', guard: 'node --test test/config-adoption.test.mjs',