11 KiB
Saved Scenario workspace: adversarial acceptance preparation
Prepared read-only against frozen renderer 68a81c3f50d846e34cae96090979543878514c1e. Authority: selected next-display policy at plan 920b718, section “Next display policy”. This is an unexecuted matrix for the next increment, not acceptance of current workspace wiring. The 87 focused foundation results belong to their eventual run receipt; none is claimed here.
Common observable guards
Before each passive navigation/display action capture full accepted Project, accepted SourceBundle, editable draft bundle, document revision, dirty flags and actual Undo/Redo behavior. Reuse app/diagram_usages/tests.rs:35-60 AuthoredState; its can_undo/can_redo booleans alone cannot prove unchanged stack contents. Add one known prior authored edit and exercise exactly one Undo/Redo after the passive sequence, as the existing navigation test does. Do not compare only saved layout fields. Selection, camera and status are expected presentation changes; raw drafts are not.
Use one owner with views A/B and an explicit derived entry. Existing resolver fixture behavior/scenario_layout/tests.rs:339 provides A (first,second)=(148,200) and B (1000,240), sharing the exchange but preserving occurrence identity. B resolves successfully with a source-order diagnostic. The lane fixture at line390 provides centers (requester,provider)=(900,-500), a provider header x=-602 and full bounds x=-660..1140. These numeric controls distinguish actual displayed geometry from a correctly labelled but wrong owner-only Scene.
Discriminating matrix
| Probe | Required observable outcome | Existing evaluator to extend |
|---|---|---|
| A → B → derived → A, same owner, including A→B without rendering an intermediate frame | Header/selector identifies the actual saved view; A shows 148/200, B shows 1000/240, derived uses the automatic positions. Whole-view and occurrence chooser results retain Saved(A), Saved(B), Derived(owner) separately. Selecting the repeated message reveals that exact occurrence in the chosen view. All common authored guards hold. | app/diagram_usages/tests.rs::repeated_scenario_messages_select_the_exact_occurrence_and_move_camera (908); app/diagram_selection_tests.rs::diagram_switch_preserves_an_independent_semantic_inspection (122). |
| Ready reveal for A; switch to B; deliver A's retained viewport/control task; deliver B's real smaller viewport | No A camera, focus or target leaks into B. B's highlighted target is visible and centered after its first actual viewport, once. Manual pan after reveal cancels any delayed snap. Use old/new documents, revisions and visit epochs independently, including identical sizes. | app/diagram_reveal/tests.rs tests at230,273,316,347: all surfaces recenter once; malformed stamps; resize before measurement; user cancellation. |
B's admitted async source-order crossing versus a saved view with self_reply=600 |
B stays authored at1000/240 with an explanatory notice. The latter produces the named typed hard failure and a COMPLETE automatic display, retaining the requested saved-view identity and its authored600. Compare normal messages, frames, lifecycle markers and nonempty typed activations against an independently captured automatic control; do not compare only one arrow or a success flag. | Resolver source controls at339 and572; new presentation-adapter child tests plus mounted workspace. Keep hard failure separate from successful diagnostics. |
| Hard failure plus a conspicuous valid lane override | Fallback uses automatic lanes AND automatic messages/scopes/activations. No surviving authored lane is mixed into fallback. Persistent banner names the saved view and says saved positions are preserved. Selection, source navigation and Fit work; geometry-dependent controls and injected mutation events are refused. Two fallback saved views remain distinct destinations even if their pictures match. | New adapter outcome assertions, actual workspace::view, existing Mounted input/pixels. scenario/usage.rs:25,156,167 must consume the displayed geometry rather than recomputing an authored-only result. |
| Invalid raw source or invalid property/behavior draft while a fallback view is already displayed | Scene continues to represent the accepted model; draft text does not silently become accepted geometry. Existing navigation/admission guards retain raw input and focus. Passive viewport, banner rendering and Fit neither apply nor cancel anything. Repair actions obey existing draft guards. Successful explicit repair changes accepted state once; Undo restores exact unsupported values and fallback deterministically. | pending_source_behavior_and_modal_inputs_block_open_and_choose_without_loss (usage tests532); scenario_workspace_viewport_events_are_passive_guarded_and_do_not_apply_raw_forms (workspace tests996); scenario_workspace_choosing_tools_preserves_valid_and_invalid_pending_editors (1042). |
| Pointer press in fallback, then source repair or view switch before release; separately replay captured Begin/Reorder events | No geometry or semantic sequence write comes from the stale pointer sequence. Clean fallback still permits semantic inspection. Merely hiding/greying controls is insufficient: directly delivered old events must be rejected. A fresh valid gesture remains a separate future acceptance gate; do not claim layout-drag support in this display increment. | canvas_workspace/tests.rs::Mounted::input/batch (140/155), captured toolbar helpers (742), Scene pointer tests and full authored guards. Requires explicit per-display capability/gesture identity to be observable through emitted events, not a test-only permission bypass. |
| Negative-origin or very wide saved geometry; Fit with actual measured viewport and visible fallback banner | All intended structural bounds fit inside the measured canvas gutter; leftmost negative header, Create marker and activation are reachable. Assert transformed known points against real widget bounds, then click one and verify exact selection. Width alone and an origin-zero camera are wrong. A zoom minimum must not silently defeat Fit. | scenario_workspace_fit_uses_measured_inner_viewport_and_rejects_nonfinite_inputs (1749), production_scenario_canvas_keeps_bounds_draft_identity_and_camera_through_editing (6), scenario_workspace_scaled_and_panned_scene_keeps_clicked_slots_and_message_endpoints (1147). |
| Delete/reidentify saved view or change owner before queued choose/Back; automatic resolution also fails; unfinished messages with latent positions | Stale identity is refusal, never automatic fallback to another owner. A valid saved view whose automatic resolution also fails shows unavailable state with inspection/source actions. Unfinished messages retain authored positions and semantic notices without invented arrows or spurious hard-failure banners. Opaque-use Back restores the original saved identity, not an arbitrary view of its owner. | revision_retarget_and_document_change_make_old_destinations_inert (usage tests461), old_panel_tokens_and_forged_targets_cannot_redirect_selection (438), existing Scenario open-use/Back tests; latent source controls at628/762. |
Minimal new test seams
Camera falsifier supplied and source-checked by the parent: with a 700x500 viewport and bounds x=-1,000,160, width=2,000,400 (height not the limiting dimension), an eight-pixel horizontal gutter requires zoom approximately 0.00034193 and pan-x 349.986; projected left/right are 8/692. The existing 0.05 floor produces 100,020 pixels of width. Assert these independently calculated endpoints after the actual Fit event, after a subsequent viewport measurement and across Fit→ZoomIn→ZoomOut. The anchor under the zoom center must remain fixed, and zoom must not jump back to 0.05. This probes the full path: Scenario::camera_zoom(None) currently passes the computed scale through zoom(Some(fitted)), clears Fit and zeros pan; shared canvas_workspace::change independently clamps 0.05. Removing only the fit_scale clamp cannot satisfy it. The toolbar must display a meaningful nonzero tiny scale (for example 0.034%, with accessible equivalent), not rounded 0%. Exact label formatting is a product choice; truthful scale and usable zoom transitions are acceptance requirements.
- The production presentation adapter itself is the necessary new seam: read-only access to requested representation, actual Scene/bounds, authored/fallback/unavailable status and diagnostics. Child tests can access its private result. Do not create a separate test renderer, fake fallback or public cache API. Test a deterministic automatic-resolution error if a real admitted fixture is available; otherwise label that branch as an unexecuted defensive condition instead of injecting a falsely representative fixture.
- Extend the existing mounted harness; it already supplies real widget layout, input batches, task execution, clicks and pixels (
behavior/canvas_workspace/tests.rs:90-300). A stable semantic widget ID for the persistent notice/source action can make geometry/focus checks practical; it is not a substitute for native reading and keyboard activation. - Extend the reveal fixture's surface cases to saved Scenarios. Preserve the actual
App::updateviewport path, rather than directly calling the new camera setter. Keep lookup completion helpers for deterministic async tests, with a later real chooser native check.
Current ownership hazards to change together: app.rs:1725,1757 Scenario context/admission; scenario.rs:71,329,350 owner-only Context/enter/Fit; scenario/workspace.rs:14,72-102 presentation and sensor identity; canvas_workspace.rs:58-87 Scope; app/diagram_reveal.rs:25-69 viewport→Surface mapping; scenario/usage.rs:25,156-201 derived-only usages/reveal. Resolver scenario_layout.rs:477-499 already distinguishes requested Representation and resolved owner; success diagnostics are currently retained in the plan, not a display policy. Line numbers are exact to 68a81c3 and should be re-resolved after integration.
Small native acceptance sequence after controller/mounted checks
At one ordinary and one narrow window size: open A/B from real diagram navigation; inspect view names and two usages of one message; Fit the negative-origin view and click its far-left marker. Open the hard-failure view, read its banner with panels shown and Focus enabled, select an element and navigate to source. Leave invalid raw source, confirm no implicit apply/loss, repair explicitly, then Undo/Redo and save/reopen. Attempt a real fallback drag and verify no source/history change. Record the failed/refused actions as well as successful ones. Native timing cannot reliably replace held-task tests, and screenshots alone cannot establish source or history preservation.
Do not broaden this increment into unrestricted movement, route editing, activation redesign, export support or generic codec acceptance. No compiler, server, GUI or source mutation was performed to prepare this note.