Make participant ports and hierarchical model navigation discoverable

Add focused containment browsing, contextual paged search, clear outline depth and direct component-port creation and connection actions. Share exact boundary feasibility checks to avoid whole-project validation for each picker candidate. Preserve source/property drafts and interrupted-save recovery state, document source-project behavior, and record Windows/Linux native verification and independent review fixes.
This commit is contained in:
2026-09-05 23:17:17 +03:00
parent 60131e610a
commit d364d3c058
16 changed files with 1829 additions and 324 deletions
+328 -40
View File
@@ -623,7 +623,7 @@ a { text-decoration: none; }
<h1 class="title">Arcadia Studio OA / SA Language and Editing Contract</h1>
<dl class="docinfo simple">
<dt class="version">Version<span class="colon">:</span></dt>
<dd class="version">2</dd>
<dd class="version">3</dd>
<dt class="status">Status<span class="colon">:</span></dt>
<dd class="status">Normative current implementation contract; first-release integration</dd>
<dt class="core-implementation">Core implementation<span class="colon">:</span></dt>
@@ -637,16 +637,14 @@ a { text-decoration: none; }
<h2>1. Scope and conformance</h2>
<p>This specification defines the shared Operational Analysis (OA) and System
Analysis (SA) model, its textual language, graphical editing obligations,
validation, persistence, and explicit migration from version 1. MUST, MUST NOT,
validation, persistence, and explicit migration from versions 1 and 2. MUST, MUST NOT,
and MAY indicate requirements, prohibitions, and permitted choices.</p>
<p>The first-release capability/mission, contextual process/chain, typed scenario
and state/event additions
are temporarily integrated under schema 2. This does not declare the first
release complete or waive its remaining scenario, state, data, transition and
usability requirements. A distinct release schema and explicit V2 migration
will be specified after those interfaces are frozen. This document describes
the current implemented records, rather than inventing compatibility rules for
that future schema.</p>
<p>Schema 3 introduces typed function and component ports, required functional
exchange endpoints, independently oriented boundary exchanges, and explicit
PortAllocation relations. First-release scenario/state additions remain in
scope; this schema change does not declare native acceptance or the wider
release complete. The preserved schema-2 reader validates the original
contract before migration.</p>
<p>OA describes stakeholders, operational work and needs. SA describes the
selected system boundary, external actors, functions, services and expected
behavior. SA permits at most one System of Interest; its absence is an
@@ -664,7 +662,7 @@ Actual verification evidence is separate from this contract.</p>
</section>
<section id="one-authoritative-model">
<h2>2. One authoritative model</h2>
<p>A Project stores <span class="docutils literal">schema_version</span> (2), <span class="docutils literal">id</span>, <span class="docutils literal">alias</span>, <span class="docutils literal">name</span>,
<p>A Project stores <span class="docutils literal">schema_version</span> (3), <span class="docutils literal">id</span>, <span class="docutils literal">alias</span>, <span class="docutils literal">name</span>,
<span class="docutils literal">description</span>, and maps named <span class="docutils literal">elements</span>, <span class="docutils literal">relations</span>, <span class="docutils literal">diagrams</span>,
<span class="docutils literal">artifact_links</span>, and <span class="docutils literal">traces</span>. Each map is keyed by the UUID contained in
its record. Duplicate map keys and mismatching keys/identities are errors.</p>
@@ -738,8 +736,8 @@ lists all additional fields. <span class="docutils literal">ref</span> means <sp
<td><p><span class="docutils literal">[]</span></p></td>
</tr>
<tr><td><p><span class="docutils literal">oa_port</span>, <span class="docutils literal">sa_port</span></p></td>
<td><p><span class="docutils literal">direction</span>: In, Out, InOut; <span class="docutils literal">interface</span>: ref?</p></td>
<td><p>InOut; none</p></td>
<td><p><span class="docutils literal">contract</span>: required PortContract; <span class="docutils literal">legacy</span>: optional LegacyPortContract</p></td>
<td><p>No implicit contract; <span class="docutils literal">legacy</span> defaults none. OA admits Legacy only.</p></td>
</tr>
<tr><td><p><span class="docutils literal">oa_scenario</span>, <span class="docutils literal">sa_scenario</span></p></td>
<td><p><span class="docutils literal">viewpoint</span>: ScenarioViewpoint; <span class="docutils literal">context</span>: ref?; <span class="docutils literal">precondition</span>, <span class="docutils literal">postcondition</span>: strings; <span class="docutils literal">steps</span>: occurrence refs</p></td>
@@ -853,7 +851,7 @@ contain activities; functions contain functions. Capability grouping uses
same-layer capability parents; it does not mean inheritance or inclusion.
Operational and system missions are root semantic records; they do not own
capabilities by containment. MissionCapability expresses that association.
DataFields belong to ExchangeItems or Record DataTypes. Ports belong to
DataFields belong to ExchangeItems or Record DataTypes. Preserved generic ports belong to
participants or work. Lifelines belong to scenarios. State-machine ownership
uses participant → machine → region → state/mode; a state or mode can contain
a nested region. All other state vertices belong to Regions as well. Events
@@ -889,11 +887,15 @@ references. Their kind is the declaration keyword:</p>
</tr>
<tr><td><p><span class="docutils literal">functional_exchange</span></p></td>
<td><p>SA function → SA function</p></td>
<td><p><span class="docutils literal">source_port</span>, <span class="docutils literal">target_port</span> / none; <span class="docutils literal">items</span> / []</p></td>
<td><p>Required <span class="docutils literal">source_port</span>, <span class="docutils literal">target_port</span> refs; <span class="docutils literal">items</span> / []</p></td>
</tr>
<tr><td><p><span class="docutils literal">boundary_exchange</span></p></td>
<td><p>Participant-owned output-compatible port → input-compatible port</p></td>
<td><p><span class="docutils literal">items</span> refs / []</p></td>
<td><p>Different participant owners; typed component ports have matching kinds</p></td>
<td><p><span class="docutils literal">oriented</span> bool / true; <span class="docutils literal">items</span> refs / []</p></td>
</tr>
<tr><td><p><span class="docutils literal">port_allocation</span></p></td>
<td><p>SA Component port → SA FunctionInput/FunctionOutput port</p></td>
<td><p>Requires the function's actual work Allocation to that component</p></td>
</tr>
<tr><td><p><span class="docutils literal">capability_involvement</span></p></td>
<td><p>Participant, activity/function, or OA role → capability</p></td>
@@ -967,12 +969,68 @@ advisory warning; it does not prevent saving the model. Shared roles do not
imply a unique performer.
Allocation of a non-leaf work element is invalid. A computed graphical summary
of its allocated leaves MUST NOT create an extra semantic allocation.</p>
<p>Functional ports, when selected, belong to the corresponding endpoint function
and have compatible direction. Boundary ports belong to participants and have
compatible direction. An interface groups ExchangeItems; an exchange using
that port interface must carry items supported by it. Studio's OA boundary
exchange is a simplified representation of operational communication; it is
not a claim of complete Capella CommunicationMean equivalence.</p>
<p>Typed function ports belong directly to SA Functions. <span class="docutils literal">FunctionOutput</span>
declares <span class="docutils literal">outgoing_items</span>; <span class="docutils literal">FunctionInput</span> declares <span class="docutils literal">incoming_items</span>.
The lists contain distinct same-layer ExchangeItem references. A functional
exchange MUST reference output/input ports owned by its respective functions,
and each carried item MUST fit both declarations. Empty declarations accept
no items. Item compatibility is identity equality; future specialization is
not inferred from names. Port declarations remain independent of incident
exchanges after creation.</p>
<p>A <span class="docutils literal">Component</span> contract belongs to an SA System or SystemActor and stores
<span class="docutils literal">kind</span> (Standard or Flow) and <span class="docutils literal">orientation</span> (Unset, In, Out, InOut).
Standard requires Unset. Flow Unset is an incomplete draft, not InOut. Typed
component connections require matching kinds: Standard/Flow mismatch is an
error. Standard connections normally use <span class="docutils literal">oriented false</span>; directional Flow
connections use true. Boundary orientation is separate from endpoint kind and
orientation. Oriented Flow endpoints permit Out at source and In at target;
unoriented Flow endpoints must permit at least one communication direction.</p>
<p>Provided/required interfaces are separate identified relations. A Standard
partner satisfies a requirement by providing the identical interface; stored
source/target order does not infer service roles. Component carried items must
fit the declared interface-item union when explicit declarations exist. An
explicitly empty interface is not a wildcard. Missing declarations and
unsatisfied required interfaces produce located completeness warnings.</p>
<p><span class="docutils literal">PortAllocation</span> relates a Component port to a typed function port only when
that function has an actual Allocation to the component owner. Standard may
allocate either function direction; Flow directions must agree. A typed
ExchangeAllocation requires both corresponding PortAllocations and compatible
carried items. Unoriented boundary exchanges allow reversed correspondence.
Visual allocation nesting is never evidence of a stored allocation relation.</p>
<p><span class="docutils literal">Legacy</span> requires <span class="docutils literal">legacy { direction <span class="pre">...;</span> interface <span class="pre">...;</span> }</span>. It preserves
generic direction/interface data and emits a located compatibility warning.
It is an explicit compatibility constructor in JSON/source; ordinary creation
pickers do not offer it. Resolution to a typed SA contract retains this record
without treating it as another active constraint. OA generic ports remain
Legacy; OA Interactions stay activity-to-activity without function ports.
Deleting a referenced legacy interface clears that optional live reference.</p>
<p>A source-3 functional exchange MUST specify both port aliases; the parser does
not create endpoints. Graphical/command FE creation plans ports atomically,
using unique compatible typed-port reuse or deterministic new endpoints.
Port deletion cascades to incident FEs; FE deletion retains independent ports.</p>
<p>For example:</p>
<pre class="literal-block">arcadia 3;
project ports_example &quot;Typed function contract&quot; {
sa_function send &quot;Send&quot; {}
sa_function receive &quot;Receive&quot; {}
sa_exchange_item payload &quot;Payload&quot; {}
sa_interface old_service &quot;Preserved service&quot; { items [&#64;payload]; }
sa_port output &quot;Output&quot; {
parent &#64;send;
contract { FunctionOutput { outgoing_items [&#64;payload]; }; };
}
sa_port input &quot;Input&quot; {
parent &#64;receive;
contract { FunctionInput { incoming_items [&#64;payload]; }; };
}
functional_exchange message &quot;Message&quot; {
source &#64;send; target &#64;receive;
source_port &#64;output; target_port &#64;input; items [&#64;payload];
}
sa_port preserved &quot;Unresolved original&quot; {
parent &#64;send; contract Legacy;
legacy { direction InOut; interface &#64;old_service; };
}</pre>
<p>An exclusive region has at most one initial state. Missing initial state is
a completeness warning. Initial states have no incoming transitions and at
most one outgoing transition; their outgoing transition has no trigger/guard.
@@ -1224,9 +1282,24 @@ Studio profile; no running history state is persisted or synthesized.</p>
<section id="diagrams-artifacts-and-cross-layer-traceability">
<h2>5. Diagrams, artifacts, and cross-layer traceability</h2>
<p><span class="docutils literal">diagram</span> has name, identity, alias, description, <span class="docutils literal">layer</span> (Oa by default),
and <span class="docutils literal">projection</span> (Overview by default). Other projections are Decomposition,
Allocation, and Traceability. Traceability diagrams can show both layers;
<span class="docutils literal">projection</span> (Overview by default), and <span class="docutils literal">routes</span> (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.</p>
<p>Allocation is an architecture view: it shows participants and work, nests
allocated work under its performer, and displays interactions, functional
exchanges and boundary exchanges. FunctionalDataflow shows the work hierarchy
and its exchanges. Capabilities shows participants, roles, missions and
capabilities with their involvement, exploitation, include, extend and
generalization links. Decomposition and Overview retain general containment;
Overview also exposes all relationships. Changing the projection hides
inapplicable placements without deleting them or changing semantic records.</p>
<p>Ports are calculated boundary anchors of visible owners, with their existing
semantic identity. An explicitly placed port is not rendered as a separate
large node. Functional exchanges use their declared source/target ports;
boundary exchanges attach to their participant ports even if those ports have
no separate placement. Collapsing an owner hides its interior; projected links
may terminate at a visible ancestor without creating a new port or exchange.</p>
<p><span class="docutils literal">node</span> has an alias and identity but no display-name string. Required fields
are <span class="docutils literal">diagram</span> and <span class="docutils literal">element</span> references. Optional fields are <span class="docutils literal">position</span>
(record with x/y, default 0/0), <span class="docutils literal">size</span> (width/height, default 224/94), and
@@ -1237,6 +1310,50 @@ distinguishable from deleting it from the model.
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.</p>
<p>Each <span class="docutils literal">routes</span> entry is an EdgeRoute record with a required <span class="docutils literal">edge</span> reference
to an existing Relation or Trace, <span class="docutils literal">waypoints</span> (default <span class="docutils literal">[]</span>), and <span class="docutils literal">label</span>
(default <span class="docutils literal">none</span>). 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.</p>
<pre class="code text literal-block"><code>routes [
{ edge &#64;report_exchange;
waypoints [{ x 420; y 260; }, { x 690; y 260; }];
label { fraction 0.55; offset { x 0; y -16; }; };
}
];</code></pre>
<p>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
<span class="docutils literal">fraction</span> (finite binary32 from 0 through 1, measured along the computed
polyline's arc length) and <span class="docutils literal">offset</span> (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 <span class="docutils literal">ParseLimits.max_records</span>; pinned
points are bounded by the per-route limit and aggregate input byte limits.</p>
<p><span class="docutils literal">SetEdgeRoute { diagram, route }</span> upserts an entry by edge identity, retaining
the order and contents of unrelated entries. <span class="docutils literal">RemoveEdgeRoute { diagram, edge }</span>
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 <span class="docutils literal">EditNode</span> and source position edits are exact
record edits and do not implicitly translate pins.</p>
<p>This is an additive schema-3 field. Earlier schema-3 JSON/source that omits
<span class="docutils literal">routes</span> 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 <span class="docutils literal">edge &#64;alias</span> 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.</p>
<p><span class="docutils literal">artifact</span> has name, identity, alias, description, required <span class="docutils literal">owner</span> ref,
<span class="docutils literal">relation</span> (References, Satisfies, Verifies, Implements, DerivedFrom;
default References), <span class="docutils literal">uri</span> (default empty), <span class="docutils literal">revision</span> (optional string),
@@ -1335,7 +1452,7 @@ spellings can be used in alias positions because references begin with <span cla
<p>The grammar below uses <span class="docutils literal">*</span> for repetition and <span class="docutils literal">?</span> for optional syntax.
<span class="docutils literal">kind</span> is exactly an element/relation keyword listed above. Typed field
sets and enum constructors are closed by sections 3–5.</p>
<pre class="code text literal-block"><code>document = &quot;arcadia&quot; &quot;2&quot; &quot;;&quot; project
<pre class="code text literal-block"><code>document = &quot;arcadia&quot; &quot;3&quot; &quot;;&quot; project
project = &quot;project&quot; alias string identity? &quot;{&quot; project-entry* &quot;}&quot;
project-entry = &quot;description&quot; string &quot;;&quot; | declaration
declaration = (kind | &quot;diagram&quot; | &quot;artifact&quot; | &quot;trace&quot;)
@@ -1369,6 +1486,77 @@ Ordinary string fields require quoted strings and cannot contain an unquoted
<span class="docutils literal">&#64;alias</span> reference. Enum fields require unquoted named values or their typed
constructor records. Typed values MUST NOT nest more than 64 levels; malformed
deeply nested drafts produce a diagnostic instead of exhausting the parser stack.</p>
<section id="modular-source3-projects">
<h3>6.1. Modular source3 projects</h3>
<p>The pure <span class="docutils literal">source_project</span> core API compiles a virtual source bundle, containing
<span class="docutils literal">entry</span> and a <span class="docutils literal">files</span> map from logical paths to UTF-8 strings. Its entry is a
root-level <span class="docutils literal">.arcadia</span> file with the source3 project header above. A project entry
MAY also be <span class="docutils literal">include string &quot;;&quot;</span>. An included fragment consists only of ordinary
declarations and include statements; it MUST NOT contain another version header,
project wrapper, or project description. Includes MUST occur between declarations,
never inside a typed field value. For example:</p>
<pre class="literal-block">arcadia 3;
project rescue &quot;Rescue&quot; id &quot;00000000-0000-4000-8000-000000000001&quot; {
description &quot;Operational model&quot;;
include &quot;oa/participants.arcadia&quot;;
include &quot;oa/activities/activity_respond.arcadia&quot;;
include &quot;views/diagram_operational.arcadia&quot;;
}</pre>
<p>An include path is relative to its containing file. <span class="docutils literal">.</span> and <span class="docutils literal">..</span> segments
normalize within the virtual project root; escaping that root is an error. Bundle
keys MUST already be normalized, use forward slashes and the lowercase
<span class="docutils literal">.arcadia</span> suffix, and contain no empty path segments. Absolute, drive, UNC,
URL, backslash, control-character and non-portable Windows device paths are
rejected. Paths ending a segment in dot or space are rejected. Keys that collide
after Unicode lowercasing are rejected. References use the exact normalized
spelling; filesystems do not participate in resolution.</p>
<p>Every reachable file is compiled once, including through repeated or diamond
includes. Include cycles are errors with their chain and including location.
Missing files, duplicate JSON file keys and supplied unreachable files are
errors. The compiler MUST NOT read local files, network resources, environment
variables or the process working directory. A future native loader is responsible
for translating a directory into this explicit bundle and enforcing physical
path ownership; the pure compiler grants no filesystem access.</p>
<p><span class="docutils literal">inspect_includes(path, source, is_entry, limits)</span> supports native discovery
before all included documents are available. It uses the same strict entry or
fragment syntax parser, applies document byte/record and local include-edge
budgets, and returns normalized targets with exact directive spans. Comments and
strings that mention include syntax are not directives. A syntax failure anywhere
in the document returns located errors and no partial include list; it does not
scan arbitrary token strings after failure. Global references and engineering
validation are deferred to bundle compilation. A native loader MUST retain the
unparsed draft on failure and enforce aggregate bytes/files/depth/edges while
loading; this inspection call does not own or inspect a directory.</p>
<p>All files share the existing global alias/UUID namespace. Files add no semantic
namespace, implicit parent, allocation or port contract. Forward references work
across files. Duplicate aliases and identities report the offending declaration
and the previous declaration location, including collisions with the project.
Moving a declaration between files MUST preserve the resulting model and UUIDs.</p>
<p><span class="docutils literal">ProjectLimits</span> adds file, include-depth and include-edge budgets to the existing
<span class="docutils literal"><span class="pre">language::ParseLimits</span></span>. Model bytes, graph objects, records and ordered
references are counted across the complete bundle, never reset per file. The
default candidate policy is 256 files, depth 32 (entry depth zero), 1,024 include
edges and 64 MiB combined UTF-8 source. Other model counts remain caller policy.
Repeated include edges count toward the edge budget; file contents count once.
The compiler admits input and stabilized output against the same limits.</p>
<p><span class="docutils literal">compile_bundle</span> returns either located errors and no candidate, or a
<span class="docutils literal">CompiledSourceProject</span> with the validated <span class="docutils literal">DecodeResult</span>, stabilized bundle,
derived <span class="docutils literal">SourceIndex</span> and located engineering warnings. It inserts explicit
UUID clauses into headers that omitted them without otherwise reformatting the
input. The returned source index and diagnostics address the returned stabilized
bytes. Clients MUST retain that bundle after successful Apply; recompiling the
original buffers with missing identities would generate fresh identities.</p>
<p>Bundle diagnostics wrap the existing Diagnostic with a logical path and optional
related locations. Declaration spans, top-level field value spans, project
header/body/description spans and resolved include locations are available in
the source index. Columns remain one-based Unicode scalars within the real file;
spans never refer to concatenated or synthetic source.</p>
<p>Single-string parse/lint APIs remain filesystem-free and reject include
statements with an explicit source-bundle requirement. V1/V2 single-file imports
continue through their existing migration readers and reports. Modular projects
require source3; migration is performed before modular export, not independently
on included fragments. Modular source does not change the semantic JSON schema.</p>
</section>
</section>
<section id="canonical-output-and-source-tooling">
<h2>7. Canonical output and source tooling</h2>
@@ -1385,10 +1573,73 @@ defaults, and JSON-escaped strings. Elements are emitted in UUID order, then
relations; diagrams and their nodes follow, then artifacts, then traces.
Fields use deterministic name order. Output ends with LF. Alias values come
from the model, never from regenerating display names.</p>
<p>Comments and input whitespace are lexical trivia. Canonicalizing or applying
source does not preserve them; the editor MUST explain that behavior.
<p>Comments and input whitespace are lexical trivia to the canonical writer.
Canonical export and the current desktop single-document Apply path do not
preserve them; the editor MUST explain that behavior. This existing path does not
define an acceptable lossless authoring policy for a future project editor.
The pure project compiler in section 6.1 preserves them while inserting missing
identities. Project update/export distinctions are described below.
Camera position, selection, source cursor, and undo history are not stored
model fields. Layout and collapse state inside NodePlacement are model data.</p>
<p>The pure <span class="docutils literal">format_bundle</span> exporter uses the same typed declaration writer as
<span class="docutils literal">format</span>. It creates a small entry file, layer/category modules, separate
activity/function roots and behavior contexts, and separate diagrams with their
placements. Ports follow their owner; relations follow their source/context.
Module names derive from persistent aliases, not display names or UUID chunks.
Declarations are ordered by semantic ancestry and alias, with owners before
descendants. Case-colliding generated file names receive deterministic numeric
suffixes. When more than 240 distinct modules would be produced, aggregate
folders consolidate into category modules to avoid thousands of tiny files.
For every valid P, <span class="docutils literal"><span class="pre">compile_bundle(format_bundle(P)).decoded.project</span> == P</span>
when the caller supplies sufficient admission budgets.</p>
<p><span class="docutils literal">update_bundle(compiled, P, limits)</span> is a core source update facility. It
checks that the compiled document/index/model snapshot still agrees, preserves
existing declaration file ownership and all untouched document bytes, and
recompiles the result before returning it. New children inherit their owner's
file; new roots use canonical module rules and add an explicit entry include.
Removed declarations do not delete source files. Alias changes also update
declarations containing references to that alias. Invalid or stale updates
return errors without mutating the input workspace.</p>
<p>Updates MUST NOT canonicalize surviving declarations in full. Header, field and
nested value positions are derived from the existing syntax parser. Unchanged
fields retain their exact bytes, including field order, omitted defaults, number
and string spelling, comments, indentation and line endings. Changed literals and
references replace only the required tokens; a reference with comments between
<span class="docutils literal">&#64;</span> and its alias retains those comments. A previously omitted default is
inserted only if its semantic value changes. New fields use the existing record's
indentation and line-ending style without reformatting its other fields.</p>
<p>A constructor tag change retains shared nested fields in place. Removed fields,
constructors and declarations remove syntax tokens, retaining their comments
and whitespace as annotations in the enclosing record or file. A complete value
shape replacement likewise retains the old value's comments beside the new value.
The updater does not invent an engineering interpretation for orphaned comments.</p>
<p>List entries use stable referenced UUIDs, message-value <span class="docutils literal">field</span> references or
lifeline-binding <span class="docutils literal">referenced</span> identities when these keys are unique. Literal
entries use their literal value. Retained entries carry their authored value
text and inter-separator comments when reordered. Trivia after a preceding comma
belongs to the following entry; trivia before the next comma belongs to the
preceding entry. Separators are reconstructed before trailing line comments so
that the result remains valid syntax. Unkeyed or duplicate-key lists use source
positions. Removed-entry comments remain as annotations in the list; newly added
entries receive formatted syntax. These rules preserve comments without silently
attaching new semantic identity to an ambiguous list entry.</p>
<p>Retained declaration UUIDs and source ownership remain unchanged. Records match
by UUID, not display name or alias. The model-only <span class="docutils literal">update_bundle</span> treats an
explicit UUID replacement as removal and creation. The desktop instead uses
<span class="docutils literal">update_bundle_with_identity_map</span> with the surviving-record provenance from
<span class="docutils literal">prepare_command</span>: explicit identity edits and sequential identity swaps MUST
preserve each original declaration's owning file and attached source trivia.
New records have no original identity mapping. The mapping MUST be injective,
refer to existing records and preserve record classes. The
result is recompiled, reindexed and checked for equality with the requested
model before return. Source changes needed for undo are workspace history data;
callers should retain complete compiled workspace snapshots rather than infer
past omission/layout choices from a model-only undo.</p>
<p>Canonical modular export is a distinct, explicit operation and does not promise
original comment/layout preservation. The native editor integrates per-file
drafts and source-project saves as specified below. The pure compiler performs
no filesystem access. REST source endpoints currently exchange a canonical
model; they do not yet exchange the multi-file source bundle.</p>
<p><span class="docutils literal">parse</span> returns a complete structurally validated candidate or error
diagnostics and no model. Engineering warnings alone MUST NOT reject a valid
candidate. Parsing may stop at the first syntax/conversion error. Diagnostics
@@ -1431,8 +1682,9 @@ placement move are different transactions. The UI MUST identify which operation
will occur. Reparenting work must not silently change its performer. A computed
parent summary must remain distinguishable from direct allocation. A collapse
operation hides presentation detail without deleting the children.</p>
<p>Applying complete source is one model transaction. Graphical edits regenerate
the matching canonical source. Both paths validate their candidate before
<p>Applying a reachable source bundle is one model transaction. Graphical edits
update its owning declarations through the source-preserving updater. Both
paths validate their candidate and model/source equality before
adoption; failure preserves the committed model, save baseline, and history.
Incomplete but structurally safe references and coverage gaps produce warnings
that allow continued authoring and saving.</p>
@@ -1440,7 +1692,11 @@ that allow continued authoring and saving.</p>
MUST NOT be replaced by generated text or lost on ordinary navigation. Undoing
source-buffer edits and undoing committed model changes are distinct operations.
Successful source Apply creates one model-history entry. Undo/redo restores
the full stored model, including aliases, trace links, and occurrence identity.</p>
the full stored model and exact accepted source bundle, including aliases,
trace links, occurrence identity, comments and file ownership. Each open file
retains its own unapplied draft across file navigation. Located lint diagnostics
refer to the raw draft bytes rather than an identity-stabilized intermediate.
Property editing MUST NOT silently discard a pending source draft.</p>
<p>Changing a graphical UUID must atomically update map keys and every reference.
Alias changes preserve UUID identity. Deletion must account for owned elements,
incident relationships, ordered occurrences, and diagram placements without
@@ -1449,14 +1705,24 @@ exchanges it references. Cross-layer counterparts are not owned descendants.
Text edits describe a complete candidate and must remove or repair dependencies
explicitly before Apply succeeds.</p>
</section>
<section id="persistence-and-v1-migration">
<h2>9. Persistence and V1 migration</h2>
<section id="persistence-and-explicit-migration">
<h2>9. Persistence and explicit migration</h2>
<p>Loading detects JSON from its first non-whitespace <span class="docutils literal">{</span>; other input is parsed
as Arcadia source regardless of filename. Saving an <span class="docutils literal">.arcadia</span> suffix uses
canonical V2 text; other suffixes use V2 JSON. Both forms preserve the same
the accepted V3 source bundle (<span class="docutils literal">arcadia 3</span>); other suffixes use V3 JSON. Both forms preserve the same
complete model. JSON rejects unknown fields throughout typed payloads,
duplicate maps, unsupported versions, and structural errors.</p>
<p>During first-release schema-2 integration, old Scenario bodies without a
<p>Native source Save All checks the original contents of observed dependencies
before staging writes. Existing target files are replaced individually and the
entry is installed last. A transaction journal records originals and intended
writes; explicit recovery restores originals only when current bytes match a
known transaction state. Unrelated external contents MUST NOT be overwritten
by recovery. Missing entry files and lint updates MUST NOT hide a detected
pending-recovery action. This is not cross-process compare-and-swap or atomic
multi-file visibility. Save failures retain the accepted model and drafts.
Detached dirty buffers MUST be reattached or reverted before source saving;
JSON export does not replace the source project's saved baseline or location.</p>
<p>The frozen schema-2 reader retains staged compatibility defaults. Old Scenario bodies without a
<span class="docutils literal">viewpoint</span> retain MixedLegacy, and old Exchange occurrence bodies without
<span class="docutils literal">message_kind</span>, <span class="docutils literal">reply_to</span> or <span class="docutils literal">values</span> retain Asynchronous, none and [].
Old StateTransition bodies without <span class="docutils literal">transition_kind</span>, <span class="docutils literal">triggers</span> or
@@ -1469,8 +1735,30 @@ defaults without changing any prior identity, name, reference, order, geometry,
artifact or trace field. The original V2 golden source/JSON in
<span class="docutils literal"><span class="pre">crates/oa-core/tests/fixtures</span></span> MUST NOT be regenerated from the newer demo;
they verify that every old persistent field survives. Canonical output writes
the added defaults. A future schema-3 adapter remains a separate required
migration and is not implied by this temporary compatibility behavior.</p>
the added defaults through the explicit schema-3 migration.</p>
<p>The V2 adapter preserves every original identity, alias, prose, parent,
placement, trace and artifact. Generic ports become Legacy with their original
direction/interface record. Existing FE endpoint IDs remain unchanged. For
only a missing endpoint it creates an item-bearing FunctionOutput/Input owned
by the already specified function, using UUID v5 in the project namespace
with <span class="docutils literal"><span class="pre">v2-fe-port:&lt;fe-id&gt;:source</span></span> or <span class="docutils literal"><span class="pre">v2-fe-port:&lt;fe-id&gt;:target</span></span>. Every
identity category is checked for collision; collisions are located errors,
never overwrites. New aliases follow the deterministic collision-free alias
convention. Every old boundary exchange receives <span class="docutils literal">oriented true</span>; no
PortAllocation is inferred.</p>
<p><span class="docutils literal">decode_with_report</span>, <span class="docutils literal">load_with_report</span> and their limited/source variants
return the model plus a migration report containing original unresolved port
IDs, required resolutions, generated endpoint IDs/aliases/owners, and boundary
orientation defaults. REST import, replacement, candidate validation and
project snapshots surface the same report. Opening an old server envelope
preserves its resource ID/revision and bytes; the next explicit edit writes
schema 3. Model/source exports contain only the canonical model. Migration
reports are transient response metadata, not persisted model facts.</p>
<p>Admission checks account for generated objects and their references before
construction. Both the old V2 contract and the resulting V3 graph are
validated. Migration itself does not save or increase a stored revision.
Canonical second saves are stable. V1 normalization produces the frozen V2
model and then uses this same V2-to-3 stage.</p>
<p>Save validates a snapshot, writes a sibling temporary file, flushes it, and
atomically replaces the destination. Failed serialization or validation must
not truncate a previous file. External concurrent-writer conflict resolution
@@ -1486,20 +1774,20 @@ step becomes a distinct occurrence element owned by its process. Generated
diagram, node, and occurrence identities use UUID v5 with the old Project ID
as namespace and deterministic role/index names. If such an identity collides,
a deterministic suffix is tried. Reimporting the same fully identified V1
input therefore yields the same V2 result, including repeated occurrences.
input therefore yields the same V3 result, including repeated occurrences.
No System or SA content is invented during an OA-only migration.</p>
<p>V1 source aliases are retained when valid in V2. JSON imports generate the old
canonical aliases deterministically. Any alias newly reserved in V2 is changed
by a deterministic suffix; aliases were not persistent V1 semantic data.
A project alias and aliases for new records are generated without colliding
with retained aliases. The existing bundled V1 text and JSON migrate to equal
V2 projects. Future versions remain errors until a separate migration exists.</p>
V3 projects. Future versions remain errors until a separate migration exists.</p>
</section>
<section id="complete-authoring-example">
<h2>10. Complete authoring example</h2>
<p>This document intentionally omits IDs for new authoring. Apply and Save write
the assigned UUIDs explicitly.</p>
<pre class="code text literal-block"><code>arcadia 2;
<pre class="code text literal-block"><code>arcadia 3;
project harbor &quot;Harbor assistance&quot; {
oa_entity control &quot;Harbor control&quot; {}
oa_activity assess &quot;Assess assistance&quot; {}