Files
ARCADIA-Studio/harness/experiments/scenario-workspace-preparation-20260913/identity-review.md
T

9.6 KiB
Raw Blame History

Saved Scenario identity routing: independent source critique

Read-only review of frozen renderer68a81c3f50d846e34cae96090979543878514c1e and plan920b718f62c20ba3c58f664dae3c5ec6b0e65ce6 (harness/scenario-renderer-bridge.md). No source changes, compilation or native evidence. Line references below are relative to crates/oa-desktop/src/ at68a81c3. These are next-integration hazards, not claims that the still-disabled saved workspace currently admits them.

Recommendation: Page::Behavior is the smallest reasonable routing candidate, following the existing saved-state adapter. Explicit Representation must be authoritative across entry, events, Back, presentation and usages; changing only canvas_workspace::Scope is insufficient. Retain semantic owner and selected IDs independently of saved Diagram identity. A new page does not remove these shared assumptions and would duplicate form/canvas dispatch, so no materially better page-level alternative is established.

Concrete unsafe assumptions and minimum changes

  1. Entry is not a single setter. app.rs:1757–1791 currently refuses saved pick/reveal before Apply. Removing that check alone reaches Diagram choice (3093–3119), which sets active_diagram and calls the architecture fit, without routing to Behavior. app/state_diagram.rs:12–42 only recognizes StateMachine. behavior.rs:1041–1051 resolves pending owner, active owner, selection ancestry, then the first behavior; a selected Diagram UUID has no semantic ancestry (1078–1107). Page(Behavior) can preselect an owner (app.rs:2454–2462). Add one guarded saved-Scenario entry/reconciliation adapter: validate Diagram→Scenario/layer, set Representation::Saved, matching active_diagram and active_owner atomically, then retire canvas/input lifetime and render the resolved scene. Never let a missing/retargeted saved view fall through to the first Scenario. Keep architecture mutation/export refusal independently scoped; enabling rendering does not authorize those operations.

  2. Same-owner views alias at several retained and passive boundaries. Context has document/scenario/revision/epoch only (behavior/scenario.rs:71–76). Presentation identity and enter use(document,owner) (329–339); workspace sensor key also omits view (scenario/workspace.rs:75–127). Shared Scope and control IDs omit view (behavior/canvas_workspace.rs:60–100); toolbar validation explicitly assumes scope.owner==context.scenario (app.rs:1284–1288). Do not put Diagram UUID into Scope.owner as a shortcut. Add representation identity alongside semantic owner and propagate it through Scope::current, Intent validation, widget IDs, sensor keys, Context and camera boundary (app.rs:1341–1355). In particular, Viewport/Scrolled/FormShown accept owner/revision while intentionally ignoring epoch (1810–1857), and Restored relaxes revision too. They must check representation. A stable navigation-visit token is also needed for A→B→A without an intervening rendered frame; keying sensors by canvas_epoch would cause feedback because viewport/pan themselves change that epoch. Keep existing epoch relaxation within the same valid visit.

  3. Back stores the caller owner, not its representation or active diagram. Caller/DetailsRestore (scenario.rs:218–239) and open_use/valid_caller/back/restore (377–451) currently carry owner, occurrence, target, camera and scroll. App OpenUse/Back (1921–1971) forcibly restore active_owner before select to defeat its same-UUID fast path; keep that fix. Capture caller Representation and destination visit plus any active_diagram value that the saved transition replaces. Opening a definition should deliberately enter Derived(callee) unless the user explicitly chooses a saved callee view; do not guess among saved views. Back must validate that the captured saved Diagram still exists and projects the captured owner, as well as the existing ScenarioUse target predicate. Retarget/deletion must refuse/skip that caller frame with the existing unavailable feedback, not silently restore automatic geometry. Rename with stable identity can still return. Restore page/representation/active_diagram/owner independently of whether selection already equals the occurrence. Back restores presentation values into a fresh visit; it must never revive the caller's old event token. Scroll observer identity (behavior.rs:38–205) and delayed form focus Stamp (scenario/focus.rs:11–37) need the same destination check when they can survive navigation.

  4. active_diagram is also remembered architecture state, not a reliable Behavior selector. Current derived Scenario reveal/OpenUse leave active_diagram untouched (app/diagram_usages.rs:381–393, app.rs:1921–1961). Page(Diagram) deliberately replaces non-architecture views with an architecture default (2435–2452). sync only checks active diagram existence/layer (792–805); Apply's UUID-following updates active_diagram/active_owner/state diagram but knows no new Representation (853–925). Introduce an explicit consistency rule: visible Saved(d) implies active_diagram==d and projection owner matches; visible Derived must not retain an unrelated saved Scenario as its apparent current view. Preserve remembered architecture where useful, but never infer Saved solely from a lingering active_diagram. Reconcile after property UUID edits, source adoption, Undo/Redo, deletion, projection-owner retarget, duplication and layer changes. Any identity change must invalidate pending gestures/measurements even when owner or selected UUID remains equal. A shared accessor returning validated(owner,representation) is smaller and safer than independently rebuilding that pair in each caller.

  5. Passive routing must preserve pending semantics and selection. apply (app.rs:853–863) refuses source drafts and pending behavior forms, but can commit valid property drafts. select (1062–1093) calls Apply and can change active_owner; its equal-ID path returns early. A new entry helper must not use select/Apply merely to display a view or recover an owner. Keep explicit choice's established property-Apply policy separate from the passive routing postlude; validate a stale destination before any Apply, and preserve both valid/invalid source-child/parent form buffers on rejected navigation. Existing usage navigation requires every modal/source/property/behavior guard to be clean (app/diagram_usages.rs:16–29), and OpenUse/Back explicitly do so. Reuse those predicates for equivalent new paths. Saved-view selection remains either a semantic object or an explicitly inspected Diagram; do not convert a message/lifeline selection into the saved Diagram just to retain workspace identity. Semantic form commands continue to address owner/occurrence IDs; view-only lane/send edits address the captured Diagram. Merely entering Saved must not change the existing Select/reorder tool into spacing-drag semantics.

  6. The current usage/reveal contract is owner-only and automatic. Scenario usage builds Scene::sequence separately, emits Surface::Scenario(owner), and Whole uses origin-zero Size (scenario/usage.rs:25–40,140–165); reveal requeries that automatic surface (167–204). collect deduplicates by subject/surface/target (diagram_usage.rs:180–207), so saved views must remain distinct surfaces. Current Surface::Saved is categorized/routed as architecture or state, and saved Scenario is explicitly refused (app/diagram_usages.rs:293–299,335–365,434–445). Introduce a typed surface→representation mapping (existing Saved(d) can remain the saved identity if every category/query/router discriminates its projection). Use the same resolved representation and full bounds for paint, query, Choose revalidation, focus and camera. Scenario viewport→surface currently hardcodes Scenario(owner) (app/diagram_reveal.rs:49–54); it must report the selected Saved(d), and final deferred reveal must verify current representation plus visit before consuming a measurement. Otherwise A/B with the same owner can jump using automatic/A bounds or satisfy the wrong first-layout completion. Keep one-surface Choose revalidation and existing async token/revision release rules. Measured-label/operation visibility remains the separately documented dependency, not something identity wiring alone solves.

Ordered narrow integration and discriminating controls

First establish validated representation/owner access, entry/reconciliation and all context/retained-widget stamps, while preserving existing semantic forms and architecture refusal. Then carry complete caller representation through OpenUse/Back and shared scroll/focus ownership. Finally wire the resolved Scene and explicit surface into paint/query/deferred reveal together; never enable a Saved chooser row whose actual reveal still rebuilds automatic geometry. Full bounds/fit changes remain necessary at that last step; the current0.05 floor/origin-zero fit is already recorded in the bridge note.

Critical controls for the parallel acceptance matrix: two saved views of one owner with equal viewport size; A→B→A before a render with held Viewport/Scrolled/toolbar/FormShown; SavedA→DerivedC→Back while an edit has already selected the caller UUID; saved caller deleted or retargeted during the visit; valid/invalid pending source-child and property buffers when selecting another view; semantic occurrence rename/UUID edit and saved Diagram UUID edit followed by Undo/Redo; asynchronous Choose for SavedA while SavedB/Derived is displayed; negative-origin Saved Whole reveal after the first exact measurement. All refused/passive cases need whole Project/source/history equality and retained raw draft checks. A separate page would still require all these checks.