Preserve editable routes across source projects and REST

This commit is contained in:
2026-09-05 20:19:38 +03:00
parent 4e5a88dc4d
commit a7c6f9678c
8 changed files with 718 additions and 6 deletions
+53 -1
View File
@@ -622,7 +622,8 @@ Studio profile; no running history state is persisted or synthesized.
----------------------------------------------------
``diagram`` has name, identity, alias, description, ``layer`` (Oa by default),
and ``projection`` (Overview by default). Other projections are Decomposition,
``projection`` (Overview by default), and ``routes`` (empty list by default).
Other projections are Decomposition,
Allocation, FunctionalDataflow, Capabilities, and Traceability. Traceability diagrams can show both layers;
other diagrams show their chosen layer.
@@ -653,6 +654,57 @@ Changing a node's diagram preserves the placement identity, alias, coordinates,
size, collapse state, and attached artifacts. The destination must accept the
element's layer and must not already contain another placement of that element.
Each ``routes`` entry is an EdgeRoute record with a required ``edge`` reference
to an existing Relation or Trace, ``waypoints`` (default ``[]``), and ``label``
(default ``none``). Route records have no separate UUID or alias. An edge occurs
at most once in a diagram's route list; different diagrams MAY store independent
routes for the same edge. List order and explicit empty entries are retained by
source and JSON round trips, although list order has no routing meaning. Edges
need not currently be visible in their diagram.
.. code-block:: text
routes [
{ edge @report_exchange;
waypoints [{ x 420; y 260; }, { x 690; y 260; }];
label { fraction 0.55; offset { x 0; y -16; }; };
}
];
Waypoints are pinned absolute diagram positions in source-to-target traversal
order; at most 128 are allowed per edge. Computed endpoints and automatic elbows
MUST NOT be persisted as authored waypoints. A label record requires both
``fraction`` (finite binary32 from 0 through 1, measured along the computed
polyline's arc length) and ``offset`` (a relative x/y diagram displacement).
All position/displacement components obey the coordinate bounds in section 2.
An absent label uses automatic placement. Unknown fields, missing required
nested fields, duplicate route edges, non-edge references and invalid numbers
are structural errors. A pinned point that obstructs routing remains stored;
geometric routing difficulty is reported separately from structural validity.
Each EdgeRoute counts as one record against ``ParseLimits.max_records``; pinned
points are bounded by the per-route limit and aggregate input byte limits.
``SetEdgeRoute { diagram, route }`` upserts an entry by edge identity, retaining
the order and contents of unrelated entries. ``RemoveEdgeRoute { diagram, edge }``
removes it; removing an absent entry is a no-op for an existing diagram and edge.
The latter is the Reset routing operation. Either command participates in the
same atomic transactions, validation and undo as other model edits. Deleting a
relation or trace removes its routes in every diagram; reidentifying it updates
route references without changing geometry. Folding, hiding, changing projection
or relocating placements MUST NOT erase routing metadata. Group movement
translates pins when both actual endpoint owners move by the same delta; label
offsets remain relative. Raw ``EditNode`` and source position edits are exact
record edits and do not implicitly translate pins.
This is an additive schema-3 field. Earlier schema-3 JSON/source that omits
``routes`` loads as an empty list. Frozen schema-1 and schema-2 readers do not
accept this new field; migration creates empty route lists. Source projects keep
routes in their owning diagram declaration/file, resolve ``edge @alias`` across
files, and retain route-entry comments by referenced edge identity when entries
are reordered. The existing REST transaction endpoint accepts the same externally
tagged command records with UUID references and conditional ETags; no separate
routing endpoint or UI-only routing model exists.
``artifact`` has name, identity, alias, description, required ``owner`` ref,
``relation`` (References, Satisfies, Verifies, Implements, DerivedFrom;
default References), ``uri`` (default empty), ``revision`` (optional string),
+85
View File
@@ -1735,6 +1735,51 @@
],
"additionalProperties": false
},
"EdgeLabel": {
"type": "object",
"description": "Authored label placement relative to the computed polyline. Both fields are required.",
"properties": {
"fraction": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Fraction of source-to-target polyline arc length."
},
"offset": {
"$ref": "#/components/schemas/Position",
"description": "Relative diagram-space displacement."
}
},
"required": ["fraction", "offset"],
"additionalProperties": false
},
"EdgeRoute": {
"type": "object",
"description": "One diagram's authored route for an existing Relation or Trace, including currently hidden edges. No separate identity. Only pinned points are stored; explicit empty routes are retained.",
"properties": {
"edge": {
"type": "string",
"format": "uuid",
"description": "Existing Relation or Trace identity, never a node placement."
},
"waypoints": {
"type": "array",
"items": { "$ref": "#/components/schemas/Position" },
"maxItems": 128,
"default": [],
"description": "Absolute diagram positions in traversal order."
},
"label": {
"oneOf": [
{ "$ref": "#/components/schemas/EdgeLabel" },
{ "type": "null" }
],
"default": null
}
},
"required": ["edge"],
"additionalProperties": false
},
"Size": {
"type": "object",
"properties": {
@@ -3445,6 +3490,12 @@
"additionalProperties": {
"$ref": "#/components/schemas/NodePlacement"
}
},
"routes": {
"type": "array",
"items": { "$ref": "#/components/schemas/EdgeRoute" },
"default": [],
"description": "Unique by edge identity within this diagram. Stored order is retained. Each entry counts against the total model record admission limit. Missing routes in earlier schema-3 models default to empty."
}
},
"required": [
@@ -4406,6 +4457,40 @@
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"SetEdgeRoute": {
"type": "object",
"description": "Upsert by edge identity; preserves unrelated route entries and order. Uses existing transaction validation and ETag checks.",
"properties": {
"diagram": { "type": "string", "format": "uuid" },
"route": { "$ref": "#/components/schemas/EdgeRoute" }
},
"required": ["diagram", "route"],
"additionalProperties": false
}
},
"required": ["SetEdgeRoute"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"RemoveEdgeRoute": {
"type": "object",
"description": "Reset to automatic routing. No-op if this existing diagram has no route for the existing edge.",
"properties": {
"diagram": { "type": "string", "format": "uuid" },
"edge": { "type": "string", "format": "uuid" }
},
"required": ["diagram", "edge"],
"additionalProperties": false
}
},
"required": ["RemoveEdgeRoute"],
"additionalProperties": false
},
{
"type": "object",
"properties": {