184 lines
20 KiB
Markdown
184 lines
20 KiB
Markdown
# Scenario layout contract candidate
|
|
|
|
Status: proposal for peer coordination; no public spelling, version, migration or gesture policy is adopted here. Scope: Scenario only; two independent saved views, lifeline x and direct message send y. This document elaborates [the selected storage direction](scenario-layout-design.md). It is not a replacement for that direction or the user specification. No core implementation or runtime verification accompanies this candidate.
|
|
|
|
## Existing facts and scope
|
|
|
|
The working baseline uses schema/source 7. Diagram already owns a representation UUID/alias and architecture placements/routes; StateMachine is an owner-bound projection precedent. Current Scenario rendering derives geometry from a semantic owner without saved lane positions. A Scenario occurrence is independently identified from its represented Exchange; a Lifeline is independently identified from its represented performer. Existing OccurrenceEndpoint contains explicit reference-occurrence IDs and an occurrence ID.
|
|
|
|
The first slice edits occurrences belonging to the chosen Scenario's own sequence tree, including its nested Fragment/Branch sequences. Here **direct** means no traversal through ScenarioUse or SequenceReference: it does not mean only top-level steps. Expanded called-scenario appearances, process topology, action/state/ref-box placement, independent receive positioning, routes, labels and resize remain outside this payload's first implementation. Their omission is not completion of #20.
|
|
|
|
## Candidate Rust shape
|
|
|
|
Names below are proposed. Preserve existing architecture JSON/source fields and defaults; do not migrate them into a new universal payload.
|
|
|
|
~~~rust
|
|
// Additional ProjectionKind variant; existing variants unchanged.
|
|
Scenario { scenario: Id }
|
|
|
|
// Additional Diagram field.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
layout: Option<DiagramLayout>,
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
enum DiagramLayout {
|
|
Scenario(ScenarioLayout),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct ScenarioLayout {
|
|
#[serde(default)]
|
|
lanes: Vec<ScenarioLanePosition>,
|
|
#[serde(default)]
|
|
messages: Vec<ScenarioMessagePosition>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct ScenarioLanePosition {
|
|
lifeline: Id,
|
|
x: f32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
struct ScenarioMessagePosition {
|
|
address: OccurrenceEndpoint,
|
|
send_y: f32,
|
|
}
|
|
~~~
|
|
|
|
There are no new row UUIDs, aliases or UUID-valued map keys. Lane row identity is its lifeline reference; message row identity is the complete address. The source declaration identity remains the Diagram UUID. Collections preserve authored order, but their order has no geometric or semantic ordering meaning.
|
|
|
|
A Scenario projection requires an existing Scenario in the Diagram's layer. It accepts layout absent/none (fully automatic), or the Scenario payload. A present empty payload is valid and distinguishable in the stored model from absence; editing must not gratuitously normalize one to the other. Other projections reject a nonempty Option regardless of whether its contained lists are empty. Scenario diagrams require empty architecture nodes and routes. Existing view_options remain governed by their existing contract; this slice neither invents scenario filter semantics nor silently applies architecture options.
|
|
|
|
Only one payload field is introduced. Future geometry should extend an appropriate typed payload, not add unrelated optional lane/message/process bags to Diagram. Existing source 7 readers must not accept the new projection or new layout field, including explicit none; the eventual version gate is a coordinated change, not an implication of serde defaulting.
|
|
|
|
## Exact address and value admission
|
|
|
|
1. A lane reference must name a Lifeline directly owned by the projected Scenario. Two Lifelines representing the same performer are distinct valid keys.
|
|
2. A message address must have references: [] and name an Occurrence whose body is Exchange. Reject any nonempty references list with an explicit unsupported-address diagnostic. Do not resolve it to its terminal occurrence or truncate it. Reusing OccurrenceEndpoint reserves the identity vocabulary, not expanded-reference support.
|
|
3. Membership follows the Scenario's actual steps and recursively its own fragment operands/branch steps, consistent with valid parent/step membership. Do not use arbitrary transitive element descendants, represented Exchange identity, or name matching. An unrelated occurrence is invalid even when it represents the same exchange. A Fragment, Action, State, ScenarioUse or SequenceReference occurrence is not a message-position key.
|
|
4. Missing sender/receiver/exchange references that are already admitted as unfinished semantic content do not themselves invalidate a correctly owned message-position row. Its authored intent may remain latent until an arrow can be rendered. The renderer must not fabricate endpoints; direct manipulation can begin only on actual geometry. Deleting the message occurrence is different from temporarily removing its endpoint assignment.
|
|
5. Reject duplicate lifeline IDs and duplicate complete message addresses after alias resolution, even when values are equal. The same address in two different Diagrams is valid. Unknown fields, missing required lifeline/address/send_y/x, malformed UUIDs and invalid nested records reject the entire candidate.
|
|
6. x and send_y are finite binary32 values with absolute value at most existing MAX_COORDINATE (1,000,000 at this baseline). Negative values and signed zero follow the same numeric admission; there is no new positive-only minimum or automatic clamp. Structural admission does not promise a feasible rendered chronology. The separately specified projection must report unsupported geometry and preserve authored intent rather than silently edit semantics or coordinates. Whether the first release refuses such an edit before commit is a separate projection/gesture integration decision.
|
|
7. Count one present payload and every lane/message entry against ParseLimits.max_records; these introduce no semantic graph objects. Reference paths are empty in this slice. Enforce shared byte/depth limits before generic allocation, then collection budgets before building membership or duplicate indexes. Validate with bounded traversal/visited identity sets and indexed membership, not one full scene traversal per entry. This counting proposal must be reconciled with #7's checked codec once; it is not a second admission system.
|
|
|
|
send_y always means the actual message's send endpoint in diagram coordinates. Normal and self messages preserve their renderer-defined send-to-receive offset for whole-message placement. In particular, a self Create/Delete currently stores a receive-oriented row: converting send_y to that row is the renderer bridge's responsibility. Store neither that internal row convention nor activation/capsule coordinates. This contract does not reorder steps, flatten parallel/alternative operands, or prescribe how a user's drag crosses another event.
|
|
|
|
## JSON and source examples
|
|
|
|
The following JSON is a complete **candidate Diagram value**, assuming the referenced Scenario/lifeline/message exist with compatible ownership and layer. Version headers belong to the containing project and are deliberately not assigned here.
|
|
|
|
~~~json
|
|
{
|
|
"id": "10000000-0000-4000-8000-000000000001",
|
|
"alias": "nominal_view",
|
|
"name": "Nominal spacing",
|
|
"description": "",
|
|
"layer": "Oa",
|
|
"projection": { "Scenario": { "scenario": "20000000-0000-4000-8000-000000000001" } },
|
|
"nodes": {},
|
|
"routes": [],
|
|
"layout": { "Scenario": {
|
|
"lanes": [ { "lifeline": "30000000-0000-4000-8000-000000000001", "x": 420.0 } ],
|
|
"messages": [ {
|
|
"address": { "references": [], "occurrence": "40000000-0000-4000-8000-000000000001" },
|
|
"send_y": 200.0
|
|
} ]
|
|
} }
|
|
}
|
|
~~~
|
|
|
|
Equivalent proposed declarations in a containing source project whose aliases resolve to those identities:
|
|
|
|
~~~text
|
|
diagram nominal_view "Nominal spacing" id "10000000-0000-4000-8000-000000000001" {
|
|
layer Oa;
|
|
projection { Scenario { scenario @nominal; }; };
|
|
layout { Scenario {
|
|
lanes [{ lifeline @controller_lane; x 420; }];
|
|
messages [{
|
|
address { references []; occurrence @delivery_first; };
|
|
send_y 200;
|
|
}];
|
|
}; };
|
|
}
|
|
diagram review_view "Review spacing" id "10000000-0000-4000-8000-000000000002" {
|
|
layer Oa;
|
|
projection { Scenario { scenario @nominal; }; };
|
|
layout { Scenario {
|
|
lanes [{ lifeline @controller_lane; x 680; }];
|
|
messages [{ address { references []; occurrence @delivery_first; }; send_y 240; }];
|
|
}; };
|
|
}
|
|
~~~
|
|
|
|
These are candidate syntax examples, not claims that the current parser accepts them. Source uses @alias in every reference field; UUID string literals remain JSON references and declaration IDs, not a source-reference shortcut.
|
|
|
|
Invalid variations: repeat controller_lane in one lanes list; repeat the same message address with another send_y; use the represented performer as lifeline; use an Exchange definition as occurrence; use another Scenario's message; use references [@scenario_use]; attach this payload to Overview; add an architecture node or EdgeRoute to this Scenario view; supply NaN/infinity or x beyond the shared bound. Each refusal preserves the entire accepted model/source/history. Equal coordinates for distinct valid lifelines are not a duplicate-key structural error; any graphical overlap feedback belongs to projection policy.
|
|
|
|
## Commands, sparse values and no-op behavior
|
|
|
|
CreateDiagram/EditDiagram remain complete-record operations. Candidate focused command names are:
|
|
|
|
~~~rust
|
|
SetScenarioLanePosition { diagram: Id, position: ScenarioLanePosition }
|
|
RemoveScenarioLanePosition { diagram: Id, lifeline: Id }
|
|
SetScenarioMessagePosition { diagram: Id, position: ScenarioMessagePosition }
|
|
RemoveScenarioMessagePosition { diagram: Id, address: OccurrenceEndpoint }
|
|
~~~
|
|
|
|
A Set validates the Scenario context/address, creates its compatible empty payload if absent, replaces an existing row in place or appends a new row, and retains unrelated rows and order. Setting an existing numerically equal value preserves the original record, including signed-zero representation; do not replace -0 with +0 during an otherwise changing batch. Explicitly setting an absent override is an authored change even if it equals today's automatic coordinate. Remove validates the existing diagram and valid semantic address before removing; removing an absent override is a no-op. Removing the final row retains a present empty payload. Explicit EditDiagram can clear it to None.
|
|
|
|
A gesture with zero actual motion never synthesizes a Set from projected coordinates. Its captured authored value, representation/document/revision/input identity and actual delta govern preview; stale/cancel/no-motion produces no write and no new Diagram. The first actual edit of a derived view prepares a fresh Diagram identity and initial override in one checked transaction; allocation during preview alone must not become a persisted record. Undo returns to the exact prior model/source absence; Redo restores the same created identity.
|
|
|
|
All command paths use existing prepare_command/execute_batch and checked source writeback. Validate the final batch; reject atomically on structural/source failure. Desktop history captures the complete compiled source snapshot. REST uses its existing If-Match, dry-run and changed:false contract. No new transaction or layout history is introduced. Only the final model/source pair decides no-op: numeric equality must not justify erasing different record provenance or source text.
|
|
|
|
## Delete, copy, retarget and identity lifecycle
|
|
|
|
| Operation | Candidate required result |
|
|
| --- | --- |
|
|
| Delete a saved Diagram | Remove that representation and its inline layout; retain its Scenario, lifelines, occurrences and other views. Existing diagram-owned artifact cleanup still applies. |
|
|
| Delete a Scenario | Include all its Scenario-projection Diagrams in the existing deletion closure. No ownerless saved view survives. |
|
|
| Delete a Lifeline | Remove its lane entries from all surviving views; existing semantic endpoint cleanup continues. Do not delete message positions merely because those messages become incomplete. |
|
|
| Delete an Occurrence, or delete a fragment/branch containing it | Prune entries for the actual deleted message IDs from all surviving views. Preserve unrelated rows. Deleting and recreating a UUID is not a rename: deleted appearance layout is not inherited by the replacement. |
|
|
| Hide/fold, change names, change represented performer/exchange | Retain authored layout keyed to the surviving appearance; visibility is not deletion. |
|
|
| Reidentify | Rewrite projection owner, lane and occurrence references (and all address components when supported) using the checked whole-project identity map; retain values, row order and source comment ownership. Preflight collisions and final context before publishing mutations. |
|
|
| Reparent within the same Scenario sequence tree | Retain position if the final model/address remains valid. Semantic step/ownership validation still applies; geometry feasibility is separately evaluated. |
|
|
| Reparent outside the Scenario, or change Exchange occurrence to a non-message body | Reject while incompatible layout remains; an explicit same-batch removal repairs it. No implicit transfer to another view or definition. |
|
|
| Retarget Scenario projection | Ordinary EditDiagram may retain only final-compatible rows. In practice a different owner requires clearing old rows explicitly in the supplied replacement; there is no hidden clearing or coordinate reassignment. Layer must also match. |
|
|
| Change Scenario projection to architecture | Explicitly remove the Scenario payload and supply valid architecture content in the same transaction. Reverse conversion explicitly removes incompatible architecture nodes/routes, including their identity/provenance handling. |
|
|
| Duplicate only a view | Create a fresh Diagram UUID and unique alias; copy payload/order/values and keep all semantic reference IDs unchanged. Do not duplicate semantic elements or externally attached artifacts implicitly. Source creates a new declaration; original comments remain with the original declaration unless a separate copy-source operation promises more. |
|
|
| Copy semantic model plus view | An explicit copy map remaps the owner and every copied appearance reference into the copied closure before final validation. Missing/incompatible mappings reject; this is not the view-only duplicate command. No new semantic-copy implementation is claimed. |
|
|
|
|
Delete/source synchronization must preserve the difference between command deletion and a malformed source edit: source that simply removes a referenced declaration while leaving stale layout is rejected. A deliberate source edit removing both is accepted atomically. Existing source identity correspondence must carry deletion/reuse and UUID swaps; never infer correspondence solely from matching final UUIDs.
|
|
|
|
## Source lifecycle and exact integration seams
|
|
|
|
At this worktree baseline:
|
|
|
|
- oa-core/model.rs:537 (OccurrenceEndpoint), 1431 (ProjectionKind), 1485 (Diagram): add only the Scenario projection and optional tagged payload; preserve existing architecture fields.
|
|
- oa-core/session.rs:24-45, 474-489: reuse checked Create/EditDiagram and add focused commands. Audit projection matches, layout and node relocation: no architecture operation may accidentally accept this new view through a wildcard.
|
|
- oa-core/language.rs:410-459 already classifies scenario, lifeline, references and occurrence as explicit references. The proposed address object nests existing reference fields; no new UUID map parser is needed. Diagram parser defaults/version gating at1817-1833 and writer at704-724 must cover layout, preserve legacy output when None, and reject the feature under old headers.
|
|
- oa-core/source_project.rs:1486 item_key currently recognizes a limited set of nested row reference keys. Extend with schema-aware lane key lifeline and message key full address, resolved through aliases and the original-to-candidate identity map. Do not key by ordinal, x/y, terminal occurrence alone or serialized text. A coordinate edit must preserve unrelated row comments, order, includes and declaration file ownership. It must not turn a source UUID rename into row deletion/reinsertion. update_bundle recompiles and checks the exact requested Project at1017-1025; retain that oracle.
|
|
- oa-core/language.rs:59-101 record limits; persistence.rs header admission/migration; validation.rs diagram checks; references.rs remap/deletion closure: integrate all new references and bounded records together.
|
|
- oa-desktop/document.rs:315-327 and366-410: unchanged checked source commit/Undo/Redo boundary. app navigation, behavior camera/view state and diagram_usage::Surface must carry saved Diagram identity separately from the semantic Scenario owner; two saved views must not alias one owner-scoped state.
|
|
- docs/language.rst Diagram/route lifecycle and docs/api.rst transaction/OpenAPI contract need coordinated normative updates only after adoption. Server may reuse generic commands, but tests must verify serialization, conditional admission, dry-run and exact no-op rather than assume automatic parity.
|
|
|
|
The future source updater should identify lane/message rows contextually inside this payload, not globally declare every arbitrary record containing a lifeline or occurrence field to be the same row type. Unknown future payload fields remain errors. Reset uses explicit removal, not magical null coordinates.
|
|
|
|
## #7 coordination and open decisions
|
|
|
|
Peer issue7 candidate847b1caa7e75bb82f6ce5193c45bcfc45973ad1a already proposes schema8 (model.rs:9), Project.definitions (1661), checked remap preflight (references.rs:214-217), and unfinished analysis source authoring (language.rs:1652-1653). Its remap function returns Result and preflights analysis before mutation. The scenario payload must join that fallible preflight, including duplicate/context validation, rather than restore an infallible signature or add a separately committing remap pass.
|
|
|
|
Coordinate one version gate/migration, generic Value admission, aggregate record/depth accounting, source nested-row identity/origin handling, reference walking and delete closure. Exact overlap files are model.rs, language.rs, source_project.rs, persistence.rs, references.rs, validation.rs and session.rs, plus their normative docs/API schema. Neither version8 nor these candidate field names are reserved by this document.
|
|
|
|
Open integration decisions: final public names and version; placement of new source row-key logic alongside peer semantic keys; migration representation of layout absence without rewriting old architecture source; the geometry feasibility contract and selected release gesture; how existing view_options are presented for Scenario views. No option here grants a new movement policy. Later expanded-reference support requires a qualified resolver, render/edit capability, path lifecycle and separate acceptance; accepting its paths now would create unsupported persisted intent.
|
|
|
|
## Required first-slice evaluators
|
|
|
|
Before claiming the contract implemented: parse/JSON/source round trips with two distinct views; same performer/exchange repeated under distinct lifeline/occurrence IDs; nested direct message admission and expanded-ref refusal; duplicate aliases resolving to identical address refusal; source comment/include preservation under coordinate edits and identity swaps; deletion/reuse, retarget/reparent repair and view-only duplication; negative/boundary coordinates without silent mutation; zero-motion signed-zero and latent projected values; first edit one-history-entry/Undo-to-absence/Redo-same-ID; stale REST and unchanged dry-run; exact send/receive and typed lifetime/activation geometry. Check original source bytes, accepted revision, undo/redo availability and other-view values on every rejected/no-op path. Pure serialization checks cannot establish gesture or native usability.
|