docs/was_pending_followup_plan.md
Epilogue to the undo/redo state-integrity epic (undo_redo_integrity_plan.md;
PRs #373, #377, #379, #380, #382). Rank-1 item from the two post-merge design
retrospectives on #382. Status: DRAFT — not scheduled.
PR 5 distinguishes two kinds of deferred scene-queue entries by origin:
interactive advertised-topic drops carry pending_intent="true" (durable
workspace intent), while layout-origin pends serialize indistinguishably from
bound layers. Because a snapshot cannot tell "this element was pending when
captured" from "this element was bound when captured", layout-origin pends
must be treated as blocking: an exact restore refuses them, and the shell
must actively maintain the invariant that committed baselines never carry
them (the #382 drain-prompt arm added by 6bec75fe after review found that
a pend-carrying baseline wedged every later exact undo).
The taxonomy is the bug's parent, not its fix: the shell patrols an invariant ("baselines are blocking-free") that a better snapshot encoding would make structural.
Serialize each queue entry's binding state, not its origin: every element
written out of the deferred queue (appendPendingRestoreElements) is stamped
was_pending="true". The restore rule then needs no taxonomy:
kExact the transaction FAILS (unchanged
semantics); under kPrompt it joins the missing-elements prompt."A committed baseline can always be exactly restored" stops being an invariant the shell maintains and becomes a property of the encoding.
Backward compatibility falls out of attribute tolerance (plan §2): old layouts carry no marker, so every element reads as was-bound → strict — which is exactly today's blocking semantics for layout-origin elements. No version bump, no migration.
A layout FILE that references topics which never arrive: elements in the file that were saved as was-pending will silently wait. Options:
pj_scene_common/scene_dock_widget.{h,cpp}
appendPendingRestoreElements: stamp was_pending="true" on every
serialized entry (one scene-side constant; also collapse the three raw
pending_intent literal sites flagged in review).unresolvedBlockingPendingRestores, clearBlockingPendingRestores
(keep unresolvedPendingRestores for diagnostics).pj_app/src/MainWindow.{h,cpp}
SceneRestoreVerdict loses blocking_topics under kExact (keep a
was-bound-missing list if UX option (b) is chosen, consumed only by the
kPrompt arms).6bec75fe drain-prompt scene arm (replaced by the marker rule
under option (b), or removed under (a)).settleSceneRestores consumers simplify accordingly.xmlLoadState/restoreLayerElement: a was-bound element that resolves
to kDeferred must surface as a failure under kExact (today "blocking"
carries that; the marker's absence carries it after).InvalidSceneElementFailsExactUndoTransactionally
(both arms — the ghost layer carries no marker, so it is was-bound-missing
and still fails the exact transaction).scene_common_test blocking/clear cases → marker semantics;
the drain-prompt scene-pend behavior per the chosen UX.1–2 days, dominated by test rewrites. Standalone PR; no dependency on the other recorded follow-ups (epoch ownership, restore template method) but lands cleanest BEFORE the template-method refactor so the transaction shape is settled first.