130 lines
7.9 KiB
Markdown
130 lines
7.9 KiB
Markdown
# Direct state editing — bounded design, 2026-09-05
|
||
|
||
Originally a read-only proposal. Implemented in the isolated state-integration
|
||
checkout through e0b2210; current verification and outstanding acceptance are in
|
||
STATE.md. The old file/line seams below describe the pre-implementation baseline.
|
||
State layouts now use the typed context and commands proposed here; full scenario
|
||
and process direct editing remain separate release work.
|
||
|
||
## Decision and first task
|
||
|
||
Reuse Diagram/NodePlacement and existing atomic commands. Prefer a typed
|
||
`ProjectionKind::StateMachine { machine: Id }` over desktop-only coordinates or
|
||
a second persisted behavior-layout store. Existing projection strings remain
|
||
valid. Use the existing constructor syntax:
|
||
`projection { StateMachine { machine @operating_modes; }; };`.
|
||
The context references a same-layer machine and scopes placements to its machine,
|
||
regions and vertices. Explicit creation initializes existing contents in one
|
||
transaction; rendering must not dirty a model. Missing placements remain legal
|
||
and discoverable through an elements-not-shown action.
|
||
|
||
Retain active behavior diagram identity independently of semantic selection;
|
||
selecting a root Event must not switch machines. Diagram context/layout are model
|
||
content; active tool, pointer and zoom remain editor state. Context participates
|
||
in reference remapping/deletion, language reference controls and OpenAPI.
|
||
|
||
The first native task is: create a region and Initial/State; drag a child and a
|
||
composite; draw a transition; reconnect its endpoint; edit its guard; undo/redo;
|
||
save/reopen and inspect the equivalent source/API records. This is the minimum
|
||
vertical slice, not acceptance of all behavior editing.
|
||
|
||
## Existing seams and implementation contract
|
||
|
||
- `behavior.rs:56,118,494`: selection-derived context, selection-only canvas.
|
||
- `behavior/state_scene.rs:24,163`: computed vertical geometry; retain its state
|
||
symbols, internal-transition rows and typed routing, but consume persisted
|
||
shared node geometry. Generic projection drops equal-anchored/self edges and
|
||
includes relationships unsuitable for a state diagram.
|
||
- `behavior/states.rs:73,191`: reuse existing typed transition controls and
|
||
candidate validation. Reconnection edits a clone of the original relation.
|
||
- `model.rs:1235,1247`, `projection.rs:104,184`, `session.rs:562`: reuse placement
|
||
identity, semantic nesting/folding and grouped movement.
|
||
|
||
Events carry diagram identity explicitly: CreateAt(diagram, parent, kind,
|
||
position), Move(diagram, node, position), Connect(diagram, source, target),
|
||
Reconnect(diagram, relation, endpoint, vertex), EditTransition(diagram, relation).
|
||
They lower to existing CreateElement+CreateNode Batch, MoveNode,
|
||
CreateRelation/EditRelation commands. No alternate mutation API is required.
|
||
Gesture state is Idle/Move/Connect/Reconnect with transient preview and a captured
|
||
revision. Release commits once; Escape, stale revision or context change cancels.
|
||
Allocate creation IDs once per accepted plan. Drawing/hit testing/preview consume
|
||
one scene. Extend app.rs:864's draft guard to every mutation gesture.
|
||
|
||
## Required invariants
|
||
|
||
Creation targets the explicitly highlighted semantic Region. Region creation
|
||
targets a machine or stable State/Mode. Dragging never reparents. Moving a
|
||
container moves all descendant placements once, including folded descendants,
|
||
only in that diagram. Child movement preserves ownership and siblings. Resize
|
||
constraints preserve child/header space and movement cannot misleadingly suggest
|
||
ownership by another region. Entry/Exit remain region-owned boundary symbols.
|
||
|
||
Reconnect preserves relation UUID, alias, name, prose, triggers/effects and
|
||
artifact references; validate the full replacement so the old relation is not
|
||
double-counted for cardinality. Do not silently change transition kind or erase
|
||
incompatible data. Internal remains a row/self contract; External self-loops and
|
||
Local edges remain distinct. Cross-immediate-region transitions can be valid;
|
||
cross-machine endpoints reject. Existing history, boundary and orthogonal rules
|
||
in state_validation.rs remain authoritative.
|
||
|
||
Reject invalid drafts without losing them. New tools must not discard unfinished
|
||
behavior drafts. Preserve semantic selection after movement and same-card
|
||
reselection. Context references remap with UUID changes; machine deletion removes
|
||
its dedicated views without deleting cross-layer counterparts. Source-created
|
||
unplaced elements remain discoverable; cross-machine reparenting must also handle
|
||
incompatible placements atomically or reject with an actionable diagnostic.
|
||
|
||
## Partition and evidence gates
|
||
|
||
Core/context owns model.rs, projection.rs, references.rs, validation.rs and focused
|
||
tests. After type freeze, representation owns language.rs, source-project reference
|
||
handling, reST/OpenAPI and round-trip/API tests. Desktop gestures own new
|
||
behavior/state_canvas.rs and state_scene.rs. Lead integrates behavior.rs,
|
||
states.rs, app.rs and diagram context controls. Avoid a general graph framework.
|
||
|
||
Test pointer events at non-100% zoom, one history entry per gesture, exact
|
||
identities, grouped movement limited to one view, atomic rejection, context
|
||
lifecycle and source/JSON/REST equality. Native trial at 1440x900 and 1100x700 uses
|
||
two regions, a nested composite and guarded transitions. Try invalid edges into
|
||
Initial, invalid history reconnection, Escape mid-drag, a folded composite,
|
||
root-Event selection and save/reopen. Record task completion, discoverability,
|
||
ownership clarity and recoverability; no such native result is claimed here.
|
||
|
||
## Current-interface review (2026-09-06)
|
||
|
||
Independent core and desktop maps confirm the payload projection remains the
|
||
smallest persisted boundary. Preserve unit-variant JSON strings; the new value
|
||
is `{"StateMachine":{"machine":"UUID"}}`. A machine context is required for
|
||
empty and partial views; inferring it from placements cannot represent an empty
|
||
diagram, while an optional unrelated Diagram context adds invalid combinations.
|
||
Use one narrow state-view membership predicate and preserve existing projections'
|
||
legal hidden placements. Dedicated views disappear with their machine; moving
|
||
elements across machines must repair incompatible views/routes in the same Batch
|
||
or reject atomically.
|
||
|
||
The shared router now owns route pins, label placement and grouped translation.
|
||
An executed probe found that merely retaining an External self-transition yields
|
||
an 18 px out-and-back stub. Add genuine self-loop and state-symbol endpoint
|
||
geometry in the shared routing/projection path; Entry/Exit snapping must precede
|
||
draw/hit testing. Internal transitions remain rows, and folded descendants must
|
||
not create false visible self-transitions. Probe:
|
||
`D:/CodexBuilds/Capella2-source-document-contract/examples/state_projection_boundary_probe.rs`.
|
||
|
||
Before adding gestures, close existing behavior draft gaps: Start/Edit must not
|
||
overwrite an unresolved form, Submit must retain it until Document source/model
|
||
commit succeeds, and Save/Close/navigation must account for behavior drafts.
|
||
Canvas events carry the existing document/diagram/revision/epoch context and
|
||
recheck it before acceptance. Generic MoveNode message delivery selects the
|
||
current active diagram and is insufficient for delayed behavior gestures.
|
||
|
||
Keep Select, State/symbol, Region, Transition and Fit in a compact toolbar.
|
||
Create inside an explicitly highlighted eligible owner, place missing ancestry
|
||
in the same command batch, and expose unplaced semantic elements. Retain the
|
||
behavior view independently of selection, including root Events. Reconnect
|
||
clones the relation and changes one endpoint; preserve all other data and reject
|
||
invalid cardinalities. Keep form editing in a bounded panel so the canvas remains
|
||
usable at 1100×700. These are decision-ready interfaces; no implementation or
|
||
native gesture acceptance is claimed by this design record. The implemented
|
||
contract and subsequent acceptance evidence are in release-state-editing.md;
|
||
production316dc55 closes the direct state slice, while scenario/process work remains.
|