Establish OA/SA prototype baseline and first-release work

This commit is contained in:
2026-09-05 17:04:16 +03:00
commit 93ba83d321
109 changed files with 56512 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# V2 acceptance audit — 0.2.0
The delivered target is an independent Arcadia-inspired Operational Analysis and System Analysis prototype. The normative [language and editing contract](language.rst) defines its exact profile; [scope.md](scope.md) states its boundaries. This audit does not imply full Capella or SysML conformance.
| Requirement | Acceptance evidence |
|---|---|
| Rust/Iced Windows and Linux | Optimized native desktop/server builds, formatting and strict Clippy pass. Current 93 tests covered on both platforms (one final Windows suite; Linux full baseline plus focused desktop refresh). Both final staged native executables launched and inspected. |
| OA and SA semantics | 27 typed element kinds, 15 relation kinds, System/external actors, decomposition/leaf allocation/roles, capabilities/missions, sequences, data/interfaces/ports, states/modes and constraints. Core semantic/history/identity/migration tests and actual graphical workflows pass. |
| Graphical/text equivalence | One typed model/validator; persistent records and fields available in properties and canonical source. Exact strings, aliases, UUID/reference maintenance and undo verified. Native shared-perspective, placement, identity and artifact controls exercised. |
| First-class text | Syntax highlighting/live lint; invalid drafts retained; explicit Apply and separate text/model histories. Lead/reviewer operated located errors, rejected Apply, Revert and declaration navigation. |
| Nested views | Shared decomposition/allocation projection feeds canvas, Fit and SVG. Final native inspection confirms readable parent/child headers at 150%; drag/relocation and independent architecture placement behavior verified. |
| Behavioral views | Repeated occurrences, lifelines/messages/actions and explicit fragments/branches; states/regions/modes with selectable numbered transition routes/details. Native graphical creation, selection and save/reopen exercised. |
| Bidirectional transitions | Create/link/reviewed update, explicit scope and boundary choices, current/proposed conflicts, Keep/Take/merged values, stable baselines and atomic undo. Native forward/reverse creation, manual link, merge typing, retarget invalidation and reverse reuse verified. |
| Artifacts and REST | Arbitrary URI/revision/fragment/model-owner metadata; conditional transactions/query/source/transition APIs. Actual HTTP/storage tests, process/restart/auth checks and source→REST→desktop transfer pass. |
| File safety | Validated atomic saves, exact-byte external conflict detection and dirty/draft guards. Native save refusal preserved externally changed bytes; saved Windows fixtures reopened successfully in Linux. |
| Independent review | Requirements, correctness, UI/runtime and maintainability reviews completed. Valid material findings fixed and reverified; no unresolved blocking/high-impact finding. |
| Reproducibility | README build/run/test instructions, locked dependencies and CI matrix; warning-free rendered reST; equal V2 examples; Windows/Linux bundles with recorded hashes and contents. |
Detailed observed evidence is in [validation.md](validation.md), [current state](../harness/STATE.md), and the final review/native/Linux/package reports under harness. Remote CI, actual Rust 1.90, Docker/remote installation, broad GPU coverage, accessibility certification and production-scale performance remain untested; those limits are not presented as completed checks.
+986
View File
@@ -0,0 +1,986 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="Docutils 0.23: https://docutils.sourceforge.io/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Arcadia Studio model REST API</title>
<style type="text/css">
/* Minimal style sheet for the HTML output of Docutils. */
/* */
/* :Author: Günter Milde, based on html4css1.css by David Goodger */
/* :Id: $Id: minimal.css 10018 2025-03-06 16:19:15Z milde $ */
/* :Copyright: © 2015, 2021 Günter Milde. */
/* :License: Released under the terms of the `2-Clause BSD license`_, */
/* in short: */
/* */
/* Copying and distribution of this file, with or without modification, */
/* are permitted in any medium without royalty provided the copyright */
/* notice and this notice are preserved. */
/* */
/* This file is offered as-is, without any warranty. */
/* */
/* .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause */
/* This CSS3 stylesheet defines rules for Docutils elements without */
/* HTML equivalent. It is required to make the document semantics visible. */
/* */
/* .. _validates: http://jigsaw.w3.org/css-validator/validator$link */
/* titles */
p.topic-title,
p.admonition-title,
p.system-message-title {
font-weight: bold;
}
p.sidebar-title,
p.rubric {
font-weight: bold;
font-size: larger;
}
p.rubric {
color: maroon;
}
p.subtitle,
p.section-subtitle,
p.sidebar-subtitle {
font-weight: bold;
margin-top: -0.5em;
}
h1 + p.subtitle {
font-size: 1.6em;
}
a.toc-backref {
color: inherit;
text-decoration: none;
}
/* Warnings, Errors */
.system-messages h2,
.system-message-title,
pre.problematic,
span.problematic {
color: red;
}
/* Inline Literals */
.docutils.literal {
font-family: monospace;
white-space: pre-wrap;
}
/* do not wrap at hyphens and similar: */
.literal > span.pre { white-space: nowrap; }
/* keep line-breaks (\n) visible */
.pre-wrap { white-space: pre-wrap; }
/* Lists */
/* compact and simple lists: no margin between items */
.simple li, .simple ul, .simple ol, .simple > li p,
.compact li, .compact ul, .compact ol, .compact > li p,
dl.simple > dd, dl.compact > dd, dl.docinfo > dd {
margin-top: 0;
margin-bottom: 0;
}
/* Nested Paragraphs */
p:first-child { margin-top: 0; }
p:last-child { margin-bottom: 0; }
details > p:last-child { margin-bottom: 1em; }
/* Table of Contents */
.contents ul.auto-toc { /* section numbers present */
list-style-type: none;
}
/* Enumerated Lists */
ol.arabic { list-style: decimal }
ol.loweralpha { list-style: lower-alpha }
ol.upperalpha { list-style: upper-alpha }
ol.lowerroman { list-style: lower-roman }
ol.upperroman { list-style: upper-roman }
/* Definition Lists and Derivatives */
dt .classifier { font-style: italic }
dt .classifier:before {
font-style: normal;
margin: 0.5em;
content: ":";
}
/* Field Lists and similar */
/* bold field name, content starts on the same line */
dl.field-list,
dl.option-list,
dl.docinfo {
display: flow-root;
}
dl.field-list > dt,
dl.option-list > dt,
dl.docinfo > dt {
font-weight: bold;
clear: left;
float: left;
margin: 0;
padding: 0;
padding-right: 0.25em;
}
/* Offset for field content (corresponds to the --field-name-limit option) */
dl.field-list > dd,
dl.option-list > dd,
dl.docinfo > dd {
margin-left: 9em; /* ca. 14 chars in the test examples, fit all Docinfo fields */
}
/* start nested lists on new line */
dd > dl:first-child,
dd > ul:first-child,
dd > ol:first-child {
clear: left;
}
/* start field-body on a new line after long field names */
dl.field-list > dd > *:first-child,
dl.option-list > dd > *:first-child
{
display: inline-block;
width: 100%;
margin: 0;
}
/* Bibliographic Fields (docinfo) */
dl.docinfo pre.address {
font: inherit;
margin: 0;
}
dl.docinfo > dd.authors > p { margin: 0; }
/* Option Lists */
dl.option-list > dt { font-weight: normal; }
span.option { white-space: nowrap; }
/* Footnotes and Citations */
.footnote, .citation { margin: 1em 0; } /* default paragraph skip (Firefox) */
/* hanging indent */
.citation { padding-left: 2em; }
.footnote { padding-left: 1.7em; }
.footnote.superscript { padding-left: 1.0em; }
.citation > .label { margin-left: -2em; }
.footnote > .label { margin-left: -1.7em; }
.footnote.superscript > .label { margin-left: -1.0em; }
.footnote > .label + *,
.citation > .label + * {
display: inline-block;
margin-top: 0;
vertical-align: top;
}
.footnote > .backrefs + *,
.citation > .backrefs + * {
margin-top: 0;
}
.footnote > .label + p, .footnote > .backrefs + p,
.citation > .label + p, .citation > .backrefs + p {
display: inline;
vertical-align: inherit;
}
.backrefs { user-select: none; }
.backrefs > a { font-style: italic; }
/* superscript footnotes */
a[role="doc-noteref"].superscript,
.footnote.superscript > .label,
.footnote.superscript > .backrefs {
vertical-align: super;
font-size: smaller;
line-height: 1;
}
a[role="doc-noteref"].superscript > .fn-bracket,
.footnote.superscript > .label > .fn-bracket {
/* hide brackets in display but leave for copy/paste */
display: inline-block;
width: 0;
overflow: hidden;
}
[role="doc-noteref"].superscript + [role="doc-noteref"].superscript {
padding-left: 0.15em; /* separate consecutive footnote references */
/* TODO: unfortunately, "+" also selects with text between the references. */
}
/* Alignment */
.align-left {
text-align: left;
margin-right: auto;
}
.align-center {
text-align: center;
margin-left: auto;
margin-right: auto;
}
.align-right {
text-align: right;
margin-left: auto;
}
.align-top { vertical-align: top; }
.align-middle { vertical-align: middle; }
.align-bottom { vertical-align: bottom; }
/* reset inner alignment in figures and tables */
figure.align-left, figure.align-right,
table.align-left, table.align-center, table.align-right {
text-align: inherit;
}
/* Text Blocks */
.topic { margin: 1em 2em; }
.sidebar,
.admonition,
.system-message {
margin: 1em 2em;
border: thin solid;
padding: 0.5em 1em;
}
div.line-block { display: block; }
div.line-block div.line-block, pre { margin-left: 2em; }
/* Code line numbers: don't copy with drag-and-drop */
pre.code .ln { display: none; }
pre.code code:before {
content: attr(data-lineno);
font-size: small;
color: gray;
}
/* Tables */
table {
border-collapse: collapse;
}
td, th {
border: thin solid silver;
padding: 0 1ex;
}
.borderless td, .borderless th {
border: 0;
padding: 0;
padding-right: 0.5em /* separate table cells */
}
table > caption, figcaption {
text-align: left;
margin-top: 0.2em;
margin-bottom: 0.2em;
}
table.captionbelow {
caption-side: bottom;
}
/* MathML (see "math.css" for --math-output=HTML) */
math .boldsymbol { font-weight: bold; }
math.boxed, math .boxed {padding: 0.25em; border: thin solid; }
/* style table similar to AMS "align" or "aligned" environment: */
mtable.cases > mtr > mtd { text-align: left; }
mtable.ams-align > mtr > mtd { padding-left: 0; padding-right: 0; }
mtable.ams-align > mtr > mtd:nth-child(2n) { text-align: left; }
mtable.ams-align > mtr > mtd:nth-child(2n+1) { text-align: right; }
mtable.ams-align > mtr > mtd:nth-child(2n+3) { padding-left: 2em; }
.mathscr mi, mi.mathscr {
font-family: STIX, XITSMathJax_Script, rsfs10,
"Asana Math", Garamond, cursive;
}
/* Document Header and Footer */
header { border-bottom: 1px solid black; }
footer { border-top: 1px solid black; }
/* Images are block-level by default in Docutils */
/* New HTML5 block elements: set display for older browsers */
img, svg, header, footer, main, aside, nav, section, figure, video, details {
display: block;
}
svg { width: auto; height: auto; } /* enable scaling of SVG images */
/* inline images */
p img, p svg, p video,
h1 img, h2 img, h3 img, h4 img, h5 img, h6 img,
h1 svg, h2 svg, h3 svg, h4 svg, h5 svg, h6 svg {
display: inline;
}
</style>
<style type="text/css">
/* CSS31_ style sheet for the output of Docutils HTML writers. */
/* Rules for easy reading and pre-defined style variants. */
/* */
/* :Author: Günter Milde, based on html4css1.css by David Goodger */
/* :Id: $Id: plain.css 10079 2025-04-10 12:01:48Z milde $ */
/* :Copyright: © 2015 Günter Milde. */
/* :License: Released under the terms of the `2-Clause BSD license`_, */
/* in short: */
/* */
/* Copying and distribution of this file, with or without modification, */
/* are permitted in any medium without royalty provided the copyright */
/* notice and this notice are preserved. */
/* */
/* This file is offered as-is, without any warranty. */
/* */
/* .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause */
/* .. _CSS3: https://www.w3.org/Style/CSS/ */
/* Document Structure */
/* ****************** */
/* "page layout" */
body {
margin: 0;
background-color: #dbdbdb;
--field-indent: 9em; /* default indent of fields in field lists */
}
main, footer, header {
line-height:1.6;
/* avoid long lines --> better reading */
/* optimum is 45…75 characters/line <http://webtypography.net/2.1.2> */
/* OTOH: lines should not be too short because of missing hyphenation, */
max-width: 50rem;
padding: 1px 2%; /* 1px on top avoids grey bar above title (mozilla) */
margin: auto;
}
main {
counter-reset: table figure;
background-color: white;
}
footer, header {
font-size: smaller;
padding: 0.5em 2%;
border: none;
}
/* Table of Contents */
ul.auto-toc > li > p {
padding-left: 1em;
text-indent: -1em;
}
nav.contents ul {
padding-left: 1em;
}
main > nav.contents ul ul ul ul:not(.auto-toc) {
list-style-type: '\2B29\ ';
}
main > nav.contents ul ul ul ul ul:not(.auto-toc) {
list-style-type: '\2B1D\ ';
}
/* Transitions */
hr.docutils {
width: 80%;
margin-top: 1em;
margin-bottom: 1em;
clear: both;
}
/* Paragraphs */
/* vertical space (parskip) */
p, ol, ul, dl, li,
.footnote, .citation,
div > math,
table {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
h1, h2, h3, h4, h5, h6,
dd, details > p:last-child {
margin-bottom: 0.5em;
}
/* Lists */
/* ===== */
/* Definition Lists */
/* Indent lists nested in definition lists */
dd > ul:only-child, dd > ol:only-child { padding-left: 1em; }
/* Description Lists */
/* styled like in most dictionaries, encyclopedias etc. */
dl.description {
display: flow-root;
}
dl.description > dt {
font-weight: bold;
clear: left;
float: left;
margin: 0;
padding: 0;
padding-right: 0.3em;
}
dl.description > dd:after {
display: table;
content: "";
clear: left; /* clearfix for empty descriptions */
}
/* Field Lists */
dl.field-list > dd,
dl.docinfo > dd {
margin-left: var(--field-indent); /* adapted in media queries or HTML */
}
/* example for custom field-name width */
dl.field-list.narrow > dd {
--field-indent: 5em;
}
/* run-in: start field-body on same line after long field names */
dl.field-list.run-in > dd p {
display: block;
}
/* Bibliographic Fields */
/* generally, bibliographic fields use dl.docinfo */
/* but dedication and abstract are placed into divs */
div.abstract p.topic-title {
text-align: center;
}
div.dedication {
margin: 2em 5em;
text-align: center;
font-style: italic;
}
div.dedication p.topic-title {
font-style: normal;
}
/* disclosures */
details { padding-left: 1em; }
summary { margin-left: -1em; }
/* Text Blocks */
/* =========== */
/* Literal Blocks */
pre.literal-block, pre.doctest-block,
pre.math, pre.code {
font-family: monospace;
}
/* Block Quotes and Topics */
bockquote { margin: 1em 2em; }
blockquote p.attribution {
text-align: right;
margin-left: 20%;
}
/* Tables */
/* ====== */
/* th { vertical-align: bottom; } */
table tr { text-align: left; }
/* "booktabs" style (no vertical lines) */
table.booktabs {
border: 0;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.booktabs * {
border: 0;
}
table.booktabs th {
border-bottom: thin solid;
}
/* numbered tables (counter defined in div.document) */
table.numbered > caption:before {
counter-increment: table;
content: "Table " counter(table) ": ";
font-weight: bold;
}
/* Explicit Markup Blocks */
/* ====================== */
/* Footnotes and Citations */
/* ----------------------- */
/* line on the left */
.footnote-list {
border-left: solid thin;
padding-left: 0.25em;
}
/* Directives */
/* ---------- */
/* Body Elements */
/* ~~~~~~~~~~~~~ */
/* Images and Figures */
/* let content flow to the side of aligned images and figures */
figure.align-left,
img.align-left,
svg.align-left,
video.align-left,
div.align-left,
object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
figure.align-right,
img.align-right,
svg.align-right,
video.align-right,
div.align-right,
object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
/* Stop floating sidebars, images and figures */
h1, h2, h3, h4, footer, header { clear: both; }
/* Numbered figures */
figure.numbered > figcaption > p:before {
counter-increment: figure;
content: "Figure " counter(figure) ": ";
font-weight: bold;
}
/* Admonitions and System Messages */
.caution p.admonition-title,
.attention p.admonition-title,
.danger p.admonition-title,
.error p.admonition-title,
.warning p.admonition-title,
div.error {
color: red;
}
/* Sidebar */
/* Move right. In a layout with fixed margins, */
/* it can be moved into the margin. */
aside.sidebar {
width: 30%;
max-width: 26em;
float: right;
clear: right;
margin-left: 1em;
margin-right: -1%;
background-color: #fffffa;
}
/* Code */
pre.code { padding: 0.7ex }
pre.code, code { background-color: #eeeeee }
/* basic highlighting: for a complete scheme, see */
/* https://docutils.sourceforge.io/sandbox/stylesheets/ */
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
/* Epigraph */
/* Highlights */
/* Pull-Quote */
/* Compound Paragraph */
/* Container */
/* Inline Markup */
/* ============= */
sup, sub { line-height: 0.8; } /* do not add leading for lines with sup/sub */
/* Inline Literals */
/* possible values: normal, nowrap, pre, pre-wrap, pre-line */
/* span.docutils.literal { white-space: pre-wrap; } */
/* Hyperlink References */
a { text-decoration: none; }
/* External Targets */
/* span.target.external */
/* Internal Targets */
/* span.target.internal */
/* Footnote References */
/* a[role="doc-noteref"] */
/* Citation References */
/* a.citation-reference */
</style>
</head>
<body>
<main id="arcadia-studio-model-rest-api">
<h1 class="title">Arcadia Studio model REST API</h1>
<p>This is the HTTP contract for <span class="docutils literal"><span class="pre">oa-server</span></span>. The normative engineering model
and source language belong to <span class="docutils literal">language.rst</span>. <span class="docutils literal">openapi.json</span> describes the
JSON records and closed command vocabulary. The server uses the same Rust
<span class="docutils literal">Project</span>, <span class="docutils literal">Command</span>, parser/printer and validation implementation as the
desktop.</p>
<section id="identity-revision-and-writes">
<h2>Identity, revision and writes</h2>
<p>All model routes begin with <span class="docutils literal">/api/v1</span>. A hosted project's immutable service
<span class="docutils literal">resource_id</span> is different from its editable <span class="docutils literal">model.id</span>. Each stored file
contains <span class="docutils literal">{resource_id, revision, model}</span>. The revision starts at 1, increments
once per changed accepted transaction, and does not change for a no-op.</p>
<p>Existing-project writes require the exact quoted ETag returned by a project
read, for example:</p>
<pre class="literal-block">If-Match: &quot;p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1&quot;</pre>
<p>Missing preconditions return 428; stale preconditions return 412. Weak ETags,
wildcards and lists are not accepted on writes. GET of the envelope, model and
source supports <span class="docutils literal"><span class="pre">If-None-Match</span></span> and returns an empty 304 when unchanged.
Use ETags as opaque strings; revisions are unsigned integer counters in JSON.</p>
<p>Every accepted changed write is persisted before the response. A worker captures
a revision-qualified snapshot, then parses and validates its candidate outside
the catalog lock. Commit rechecks the exact ETag and atomically installs the
envelope under the lock. Another accepted edit during preparation therefore
returns 412 even when the original snapshot passed its initial revision check.
Malformed or rejected inputs preserve the accepted model. Incomplete-model
warnings permit work and are returned in <span class="docutils literal">issues</span>.</p>
<p>There is no retained revision history, idempotency receipt, tombstone or
server undo. DELETE permanently removes the hosted envelope. Source/JSON
exports contain the current model only. They preserve the model's persistent
aliases, diagrams, artifact references and OA/SA trace data. Artifact revision
strings refer to external artifacts, not past server snapshots.</p>
<p>A lost connection or 504 can occur after an admitted write has begun. That
write may still complete. Read current state before choosing a new action.
Do not automatically retry POST create/import or regenerate random IDs and
assume the original request failed.</p>
</section>
<section id="routes">
<h2>Routes</h2>
<p>Health and schema routes are public. All model routes require the configured
bearer token, if any. Non-loopback binding requires a configured token.</p>
<table>
<caption>Endpoints</caption>
<colgroup>
<col style="width: 12.0%" />
<col style="width: 41.0%" />
<col style="width: 47.0%" />
</colgroup>
<thead>
<tr><th class="head"><p>Method</p></th>
<th class="head"><p>Path</p></th>
<th class="head"><p>Behavior</p></th>
</tr>
</thead>
<tbody>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal">/health</span>, <span class="docutils literal">/health/live</span>, <span class="docutils literal">/health/ready</span></p></td>
<td><p>Responding process with successfully opened store.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal">/openapi.json</span>, <span class="docutils literal">/api/v1/openapi.json</span></p></td>
<td><p>OpenAPI 3.1 contract.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal">/api/v1/projects</span></p></td>
<td><p>Create from <span class="docutils literal"><span class="pre">{&quot;name&quot;:&quot;...&quot;}</span></span>; 201 + Location/ETag.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal">/api/v1/projects</span></p></td>
<td><p>Summaries; <span class="docutils literal">limit</span>, <span class="docutils literal">after</span>, name substring <span class="docutils literal">q</span>.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}</span></span></p></td>
<td><p>Complete current stored envelope.</p></td>
</tr>
<tr><td><p>PATCH</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}</span></span></p></td>
<td><p>Conditional rename using <span class="docutils literal"><span class="pre">{&quot;name&quot;:&quot;...&quot;}</span></span>.</p></td>
</tr>
<tr><td><p>DELETE</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}</span></span></p></td>
<td><p>Conditional permanent removal; 204.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal">/api/v1/projects/import</span></p></td>
<td><p>Raw model JSON or Arcadia source; new service ID.</p></td>
</tr>
<tr><td><p>GET / PUT</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/model</span></span></p></td>
<td><p>Export or conditionally replace native model JSON.</p></td>
</tr>
<tr><td><p>GET / PUT</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/source</span></span></p></td>
<td><p>Export or conditionally replace canonical UTF-8 source.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/elements</span></span></p></td>
<td><p>Filter by <span class="docutils literal">kind</span>, <span class="docutils literal">layer</span>, <span class="docutils literal">alias</span>, name <span class="docutils literal">q</span>.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/relations</span></span></p></td>
<td><p>Filter by <span class="docutils literal">kind</span>, <span class="docutils literal">source</span>, <span class="docutils literal">target</span>, <span class="docutils literal">incident_to</span>.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/artifact-links</span></span></p></td>
<td><p>Artifact links; optional owner <span class="docutils literal">object</span> filter.</p></td>
</tr>
<tr><td><p>GET</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/traces</span></span></p></td>
<td><p>OA/SA traces; optional endpoint <span class="docutils literal">object</span> filter.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/query</span></span></p></td>
<td><p>Bounded directed neighborhood, no mutation.</p></td>
</tr>
<tr><td><p>GET / POST</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/validation</span></span></p></td>
<td><p>Validate current model or a submitted candidate.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/transactions</span></span></p></td>
<td><p>Conditional typed command batch, optional dry run.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/transitions/preview</span></span></p></td>
<td><p>Preview a core transition against If-Match; return plan/base_revision.</p></td>
</tr>
<tr><td><p>POST</p></td>
<td><p><span class="docutils literal"><span class="pre">/api/v1/projects/{id}/transitions/apply</span></span></p></td>
<td><p>Conditionally apply that client-held plan and base_revision.</p></td>
</tr>
</tbody>
</table>
<p><span class="docutils literal"><span class="pre">Content-Type:</span> application/json</span> is required for JSON requests.
<span class="docutils literal">text/plain; <span class="pre">charset=utf-8</span></span> is used for Arcadia source. The service does not
guess formats from a filename. Unknown DTO fields, invalid UUIDs/enums, duplicate
model map keys and unsupported schemas are rejected. Maximum body size is
8 MiB. Compressed or encoded request bodies and browser Origin requests are
not supported.</p>
<p>Model admission permits at most 5,000 elements plus relations, 20,000 total
persistent records (including the project, views and placements), and 50,000
ordered step references. Source declaration counts and decoded JSON record
counts are checked before structural validation. Legacy import counts the
occurrences and placements migration will generate before validating or
migrating that model. Exceeding these budgets returns <span class="docutils literal">422 model_limit</span>;
candidate validation reports the same diagnostic in its 200 response.
Parsing, validation and query processing use snapshot workers, so this work
does not hold the catalog lock or serialize unrelated model preparation.</p>
<p>Import accepts schema-1 JSON through the shared explicit migration adapter,
as well as schema-2 native JSON. Source import uses the shared language parser
and its supported migrations. Export and native JSON replacement use schema 2.
Only documented query parameters are accepted. In particular, appending
<span class="docutils literal"><span class="pre">?revision=...</span></span> to model/source export is an error, not historical retrieval.</p>
</section>
<section id="queries-and-pagination">
<h2>Queries and pagination</h2>
<p>List filters combine using AND. Enum spelling is case-sensitive and comes
from the shared schema: examples include <span class="docutils literal">kind=Activity</span> and <span class="docutils literal">layer=Oa</span>.
Aliases match exactly; <span class="docutils literal">q</span> is a Unicode-lowercase substring match on the
display name. Empty or invalid enum values are request errors.</p>
<p>Lists are ordered by UUID. <span class="docutils literal">limit</span> defaults to 100 and accepts 1 through
1000. <span class="docutils literal">after</span> is an exclusive last UUID. <span class="docutils literal">next_after</span> is null at the end.
Project-scoped lists return a revision. Include that revision on following
pages to detect a changed model; 409 <span class="docutils literal">snapshot_changed</span> means restart the
query. Supplying a past revision does not retrieve history. The project catalog
is a live list and does not have a multi-page snapshot guarantee.</p>
<p>A neighborhood request is:</p>
<pre class="literal-block">{
&quot;roots&quot;: [&quot;1b37b9d7-9a95-4a51-8707-010878a82419&quot;],
&quot;direction&quot;: &quot;both&quot;,
&quot;relation_kinds&quot;: [&quot;Allocation&quot;, &quot;Interaction&quot;],
&quot;max_depth&quot;: 2,
&quot;max_objects&quot;: 200,
&quot;revision&quot;: 3
}</pre>
<p>Roots may identify elements or relations. The service traverses relation
source/target adjacency in the selected direction (incoming/outgoing/both),
preserves distinct parallel relations, and returns <span class="docutils literal">elements</span>, <span class="docutils literal">relations</span>,
<span class="docutils literal">revision</span> and <span class="docutils literal">truncated</span>. Empty kind filters permit all relation kinds.
Every returned relation includes its endpoint closure, including the endpoints
of a referenced relation. Closure records count against the object bound but
do not add traversal depth. Model ownership, process occurrence order and
non-endpoint references remain available in the returned records and complete
model; they are not silently treated as additional graph edges.</p>
<p>Use 1–100 roots, depth 0–4, and 1–1000 total returned objects. The cap must fit
the roots and their endpoint closure. A root closure that cannot fit is an
error; further omitted edges set <span class="docutils literal">truncated: true</span>. A missing root is 422.
Deterministic UUID iteration makes repeated queries of one snapshot stable.</p>
</section>
<section id="transactions">
<h2>Transactions</h2>
<p>The <span class="docutils literal">commands</span> array contains the shared externally tagged Serde command
representation. It is not RFC 6902 JSON Patch. For example:</p>
<pre class="literal-block">{
&quot;commands&quot;: [
{&quot;Rename&quot;: [&quot;1b37b9d7-9a95-4a51-8707-010878a82419&quot;, &quot;Assess assistance&quot;]},
{&quot;Describe&quot;: [&quot;1b37b9d7-9a95-4a51-8707-010878a82419&quot;, &quot;Review the request.&quot;]}
],
&quot;dry_run&quot;: true
}</pre>
<p>One-field variants use <span class="docutils literal"><span class="pre">{&quot;Delete&quot;:&quot;uuid&quot;}</span></span>; tuple variants use arrays;
named-field variants use objects. For a new typed OA activity:</p>
<pre class="literal-block">{
&quot;CreateElement&quot;: {
&quot;id&quot;: &quot;1b37b9d7-9a95-4a51-8707-010878a82419&quot;,
&quot;alias&quot;: &quot;assess&quot;,
&quot;name&quot;: &quot;Assess assistance&quot;,
&quot;description&quot;: &quot;Review the request.&quot;,
&quot;parent&quot;: null,
&quot;body&quot;: {&quot;Oa&quot;: &quot;Activity&quot;}
}
}</pre>
<p><span class="docutils literal">CreateArtifact</span>/<span class="docutils literal">EditArtifact</span> and <span class="docutils literal">CreateTrace</span>/<span class="docutils literal">EditTrace</span> use the same
transaction boundary as engineering edits. Artifact URI, revision and fragment
strings are opaque references. No URI is fetched or verified by the service.
Model references and alias uniqueness are checked by the core.</p>
<p>At most 200 commands are allowed, counting nested Batch wrappers. Explicit
creation identities allow later commands in the batch to reference earlier
creations. The shared <span class="docutils literal">execute_batch</span> applies all operations to one candidate
and validates the final state. A rejected operation or final structural error
rejects the whole request. Reference rewrites/deletion cleanup follow the same
core logic as desktop editing.</p>
<p>Dry run still requires <span class="docutils literal"><span class="pre">If-Match</span></span>. Its response includes <span class="docutils literal">revision</span>,
<span class="docutils literal">dry_run</span>, <span class="docutils literal">changed</span>, <span class="docutils literal">issues</span> and the proposed <span class="docutils literal">model</span>. It neither writes
nor reserves IDs. A committed response has the same fields except the model is
omitted. Commit must check the revision again. A no-op is successful with
<span class="docutils literal">changed: false</span> and the original ETag.</p>
</section>
<section id="contextual-process-and-chain-topology">
<h2>Contextual process and chain topology</h2>
<p>Operational processes and system functional chains can combine work
contributions, exchange contributions, explicit controls and references to
reusable sequences. Each contribution is an ordinary <span class="docutils literal">Occurrence</span> element
owned directly by the process/chain, with its stable ID in that owner's
<span class="docutils literal">steps</span> list. <span class="docutils literal">OccurrenceBody</span> includes, for example:</p>
<pre class="literal-block">{&quot;Action&quot;:{&quot;action&quot;:&quot;&lt;work UUID&gt;&quot;,&quot;lifeline&quot;:null}}
{&quot;Control&quot;:{&quot;kind&quot;:&quot;Choice&quot;}}
{&quot;SequenceReference&quot;:{&quot;sequence&quot;:&quot;&lt;process or chain UUID&gt;&quot;}}</pre>
<p><span class="docutils literal">ControlKind</span> is <span class="docutils literal">Choice</span>, <span class="docutils literal">Merge</span>, <span class="docutils literal">Fork</span>, <span class="docutils literal">Join</span> or <span class="docutils literal">Loop</span>.
<span class="docutils literal">RelationBody.SequenceLink</span> connects contribution IDs in one owner and
contains <span class="docutils literal">source</span>, <span class="docutils literal">target</span> and a string <span class="docutils literal">guard</span>. Conditions belong to
choice/loop paths; an explicit <span class="docutils literal">else</span> guard can describe the other path.
The readable order of the <span class="docutils literal">steps</span> list remains persistent, while links
express the explicit alternative, parallel and loop topology. The core checks
owner context, work/exchange endpoint agreement and recursive sequence reuse;
engineering diagnostics identify incomplete branch cardinality or paths.
Old exchange-only sequences without explicit links retain their linear meaning.</p>
<p>These records use the same create/edit transaction commands and source/model
replacement routes as other engineering records. For example,
<span class="docutils literal">GET <span class="pre">/api/v1/projects/{id}/relations?kind=SequenceLink</span></span> queries the links.
The source representation uses <span class="docutils literal">sequence &#64;alias</span> references. The service
does not execute conditions or simulate a process.</p>
</section>
<section id="typed-scenarios-and-message-values">
<h2>Typed scenarios and message values</h2>
<p>Scenario bodies store an explicit <span class="docutils literal">viewpoint</span>. Occurrence Exchange bodies
store <span class="docutils literal">message_kind</span>, optional correlated-call <span class="docutils literal">reply_to</span> and contextual
<span class="docutils literal">values</span>. Message kinds are Asynchronous, Call, Reply, Create, Delete, or
<span class="docutils literal"><span class="pre">{&quot;Timer&quot;:{&quot;expression&quot;:&quot;after</span> 5 <span class="pre">s&quot;}}</span></span>. Each value record contains a
carried DataField UUID in <span class="docutils literal">field</span> and its textual <span class="docutils literal">expression</span>; expressions
do not change the shared exchange item's type or evaluate server-side.</p>
<p><span class="docutils literal">ScenarioUse</span> contains a referenced <span class="docutils literal">scenario</span> and one-to-one lifeline
<span class="docutils literal">bindings</span> records with <span class="docutils literal">referenced</span> and <span class="docutils literal">local</span> UUIDs. The core validates
viewpoint/layer compatibility, call/reply order and reversal, lifetime order,
carried fields, binding identity and recursive reuse through the same candidate
boundary used by the desktop. The normative details are in <span class="docutils literal">language.rst</span>.</p>
<p>Existing schema-2 files may omit the new fields: missing viewpoint maps to
MixedLegacy and missing message fields to Asynchronous, null and []. Source
exports make those compatibility defaults explicit. This preserves original V2
models while the release schema is being integrated; it does not silently
reinterpret old mixed lifelines as a strict new viewpoint.</p>
</section>
<section id="validation">
<h2>Validation</h2>
<p>GET returns current-model diagnostics. POST accepts either:</p>
<pre class="literal-block">{&quot;format&quot;:&quot;json&quot;, &quot;model&quot;: {&quot;...&quot;:&quot;complete native Project record&quot;}}</pre>
<p>or:</p>
<pre class="literal-block">{&quot;format&quot;:&quot;arcadia&quot;, &quot;source&quot;:&quot;complete source document&quot;}</pre>
<p>A well-formed validation request returns 200 with <span class="docutils literal">valid</span> and <span class="docutils literal">issues</span> even
if its candidate is invalid. A malformed request envelope or JSON model record
is a 400. Source diagnostics include one-based line and column according to the
shared language parser. Candidate validation does not replace current state.</p>
</section>
<section id="typed-state-machines-and-events">
<h2>Typed state machines and events</h2>
<p>OA and SA Event records use the same <span class="docutils literal"><span class="pre">SharedBody::Event</span></span> and <span class="docutils literal">EventBody</span>
contract as source and graphical editing. Signal carries same-layer item
references; Time specifies After/At and an expression; Change specifies a
condition expression. State vertices include Choice, Junction, Fork, Join,
ShallowHistory, DeepHistory, EntryPoint, ExitPoint and Terminate, as well as
State, Mode, Initial and Final. Query these through the ordinary <span class="docutils literal">kind</span>
filters. No timer or executable state-machine service is implied.</p>
<p>StateTransition keeps <span class="docutils literal">trigger</span>, <span class="docutils literal">guard</span> and <span class="docutils literal">effect</span> prose and adds
<span class="docutils literal">transition_kind</span> (External/Local/Internal), <span class="docutils literal">triggers</span> (Event or work UUIDs)
and <span class="docutils literal">effects</span> (work UUIDs). The reference lists use UUIDs in JSON and aliases
in source. Missing new properties in staged schema-2 input hydrate as
External/[]/[] without changing prior text. These defaults are compatibility
behavior pending the distinct release schema, not a versionless expansion of
every future input contract.</p>
<p>Use one ordinary transaction to create an event and edit its referring
transition. Reidentification updates references, and deletion prunes signal
items and effect references. Removing the last typed trigger with no prior
trigger prose leaves a descriptive replacement reminder; it does not silently
introduce a completion transition. Invalid typed references reject the whole
candidate and preserve revision/ETag. All writes retain the same conditional
commit and disk persistence contract as other semantic records.</p>
</section>
<section id="cross-layer-transitions">
<h2>Cross-layer transitions</h2>
<p>Preview accepts the shared <span class="docutils literal">TransitionRequest</span> record, including direction
(<span class="docutils literal">OaToSa</span> or <span class="docutils literal">SaToOa</span>), sources, dependency scope, mappings, selected fields,
conflict resolutions and optional destination diagram. All fields are explicit
in OpenAPI. At most 100 sources, 200 mappings and 400 field resolutions are
accepted. A conflict-free plan must fit the same 200-command transaction limit
and model-capacity limits.</p>
<p>The preview response is <span class="docutils literal"><span class="pre">{&quot;base_revision&quot;:1,&quot;plan&quot;:{...}}</span></span>. It contains
planned changes, conflicts, warnings and exact commands/IDs. It does not write
the model or persist a server-side plan. Apply accepts that entire response and
requires the original ETag in <span class="docutils literal"><span class="pre">If-Match</span></span>. It checks both base_revision and the
core's snapshot fingerprint before executing the shared transition validator.
Stale revisions fail with 412 and unresolved conflicts with 422. Successful
apply increments the revision once, preserving the planned identities.</p>
<p>A supplied plan is a client-owned command proposal. It is not cryptographically
authenticated as an untouched server preview; the same authenticated client
can submit ordinary typed commands. The core fingerprint is a change detector,
not a security digest. There is no plan token, expiration store or receipt.</p>
</section>
<section id="error-contract">
<h2>Error contract</h2>
<p>All errors have <span class="docutils literal"><span class="pre">Content-Type:</span> application/problem+json</span>. Example:</p>
<pre class="literal-block">{
&quot;type&quot;:&quot;urn:arcadia:problem:revision_conflict&quot;,
&quot;title&quot;:&quot;Precondition Failed&quot;,
&quot;status&quot;:412,
&quot;code&quot;:&quot;revision_conflict&quot;,
&quot;detail&quot;:&quot;Project changed. Reload and review the current model before retrying.&quot;,
&quot;current_revision&quot;:3
}</pre>
<p><span class="docutils literal">issues</span> contain severity, code and message, and may include field,
object_id, line and column. Use
<span class="docutils literal">code</span> for client decisions, not the English message. <span class="docutils literal"><span class="pre">X-Request-ID</span></span> is a
response correlation UUID. This is not an idempotency key or receipt address.</p>
<ul class="simple">
<li><p>400: malformed JSON/envelope/query/UUID or unsupported ETag form.</p></li>
<li><p>401: missing or incorrect configured bearer token; includes WWW-Authenticate.</p></li>
<li><p>403: browser Origin is unsupported, or unauthenticated Host is not loopback.</p></li>
<li><p>404: missing project or route.</p></li>
<li><p>405: unsupported HTTP method.</p></li>
<li><p>409: requested query revision changed or revision counter exhausted.</p></li>
<li><p>412: stale write precondition.</p></li>
<li><p>413: body or serialized model exceeds size limit.</p></li>
<li><p>415: unsupported Content-Type or Content-Encoding.</p></li>
<li><p>422: rejected source/model/command, model count limit, or missing graph root.</p></li>
<li><p>428: missing If-Match.</p></li>
<li><p>503: work slots full or filesystem/storage unavailable; Retry-After is present.</p></li>
<li><p>504: request deadline; an admitted write outcome may be unknown.</p></li>
<li><p>507: fixed store cap of 128 models or 256 MiB serialized envelopes reached.</p></li>
<li><p>500: unexpected server/worker failure.</p></li>
</ul>
<p>Deployment and persistence limitations are documented in <span class="docutils literal">server.md</span>.</p>
</section>
</main>
</body>
</html>
+380
View File
@@ -0,0 +1,380 @@
Arcadia Studio model REST API
=============================
This is the HTTP contract for ``oa-server``. The normative engineering model
and source language belong to ``language.rst``. ``openapi.json`` describes the
JSON records and closed command vocabulary. The server uses the same Rust
``Project``, ``Command``, parser/printer and validation implementation as the
desktop.
Identity, revision and writes
-----------------------------
All model routes begin with ``/api/v1``. A hosted project's immutable service
``resource_id`` is different from its editable ``model.id``. Each stored file
contains ``{resource_id, revision, model}``. The revision starts at 1, increments
once per changed accepted transaction, and does not change for a no-op.
Existing-project writes require the exact quoted ETag returned by a project
read, for example::
If-Match: "p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1"
Missing preconditions return 428; stale preconditions return 412. Weak ETags,
wildcards and lists are not accepted on writes. GET of the envelope, model and
source supports ``If-None-Match`` and returns an empty 304 when unchanged.
Use ETags as opaque strings; revisions are unsigned integer counters in JSON.
Every accepted changed write is persisted before the response. A worker captures
a revision-qualified snapshot, then parses and validates its candidate outside
the catalog lock. Commit rechecks the exact ETag and atomically installs the
envelope under the lock. Another accepted edit during preparation therefore
returns 412 even when the original snapshot passed its initial revision check.
Malformed or rejected inputs preserve the accepted model. Incomplete-model
warnings permit work and are returned in ``issues``.
There is no retained revision history, idempotency receipt, tombstone or
server undo. DELETE permanently removes the hosted envelope. Source/JSON
exports contain the current model only. They preserve the model's persistent
aliases, diagrams, artifact references and OA/SA trace data. Artifact revision
strings refer to external artifacts, not past server snapshots.
A lost connection or 504 can occur after an admitted write has begun. That
write may still complete. Read current state before choosing a new action.
Do not automatically retry POST create/import or regenerate random IDs and
assume the original request failed.
Routes
------
Health and schema routes are public. All model routes require the configured
bearer token, if any. Non-loopback binding requires a configured token.
.. list-table:: Endpoints
:header-rows: 1
:widths: 12 41 47
* - Method
- Path
- Behavior
* - GET
- ``/health``, ``/health/live``, ``/health/ready``
- Responding process with successfully opened store.
* - GET
- ``/openapi.json``, ``/api/v1/openapi.json``
- OpenAPI 3.1 contract.
* - POST
- ``/api/v1/projects``
- Create from ``{"name":"..."}``; 201 + Location/ETag.
* - GET
- ``/api/v1/projects``
- Summaries; ``limit``, ``after``, name substring ``q``.
* - GET
- ``/api/v1/projects/{id}``
- Complete current stored envelope.
* - PATCH
- ``/api/v1/projects/{id}``
- Conditional rename using ``{"name":"..."}``.
* - DELETE
- ``/api/v1/projects/{id}``
- Conditional permanent removal; 204.
* - POST
- ``/api/v1/projects/import``
- Raw model JSON or Arcadia source; new service ID.
* - GET / PUT
- ``/api/v1/projects/{id}/model``
- Export or conditionally replace native model JSON.
* - GET / PUT
- ``/api/v1/projects/{id}/source``
- Export or conditionally replace canonical UTF-8 source.
* - GET
- ``/api/v1/projects/{id}/elements``
- Filter by ``kind``, ``layer``, ``alias``, name ``q``.
* - GET
- ``/api/v1/projects/{id}/relations``
- Filter by ``kind``, ``source``, ``target``, ``incident_to``.
* - GET
- ``/api/v1/projects/{id}/artifact-links``
- Artifact links; optional owner ``object`` filter.
* - GET
- ``/api/v1/projects/{id}/traces``
- OA/SA traces; optional endpoint ``object`` filter.
* - POST
- ``/api/v1/projects/{id}/query``
- Bounded directed neighborhood, no mutation.
* - GET / POST
- ``/api/v1/projects/{id}/validation``
- Validate current model or a submitted candidate.
* - POST
- ``/api/v1/projects/{id}/transactions``
- Conditional typed command batch, optional dry run.
* - POST
- ``/api/v1/projects/{id}/transitions/preview``
- Preview a core transition against If-Match; return plan/base_revision.
* - POST
- ``/api/v1/projects/{id}/transitions/apply``
- Conditionally apply that client-held plan and base_revision.
``Content-Type: application/json`` is required for JSON requests.
``text/plain; charset=utf-8`` is used for Arcadia source. The service does not
guess formats from a filename. Unknown DTO fields, invalid UUIDs/enums, duplicate
model map keys and unsupported schemas are rejected. Maximum body size is
8 MiB. Compressed or encoded request bodies and browser Origin requests are
not supported.
Model admission permits at most 5,000 elements plus relations, 20,000 total
persistent records (including the project, views and placements), and 50,000
ordered step references. Source declaration counts and decoded JSON record
counts are checked before structural validation. Legacy import counts the
occurrences and placements migration will generate before validating or
migrating that model. Exceeding these budgets returns ``422 model_limit``;
candidate validation reports the same diagnostic in its 200 response.
Parsing, validation and query processing use snapshot workers, so this work
does not hold the catalog lock or serialize unrelated model preparation.
Import accepts schema-1 JSON through the shared explicit migration adapter,
as well as schema-2 native JSON. Source import uses the shared language parser
and its supported migrations. Export and native JSON replacement use schema 2.
Only documented query parameters are accepted. In particular, appending
``?revision=...`` to model/source export is an error, not historical retrieval.
Queries and pagination
----------------------
List filters combine using AND. Enum spelling is case-sensitive and comes
from the shared schema: examples include ``kind=Activity`` and ``layer=Oa``.
Aliases match exactly; ``q`` is a Unicode-lowercase substring match on the
display name. Empty or invalid enum values are request errors.
Lists are ordered by UUID. ``limit`` defaults to 100 and accepts 1 through
1000. ``after`` is an exclusive last UUID. ``next_after`` is null at the end.
Project-scoped lists return a revision. Include that revision on following
pages to detect a changed model; 409 ``snapshot_changed`` means restart the
query. Supplying a past revision does not retrieve history. The project catalog
is a live list and does not have a multi-page snapshot guarantee.
A neighborhood request is::
{
"roots": ["1b37b9d7-9a95-4a51-8707-010878a82419"],
"direction": "both",
"relation_kinds": ["Allocation", "Interaction"],
"max_depth": 2,
"max_objects": 200,
"revision": 3
}
Roots may identify elements or relations. The service traverses relation
source/target adjacency in the selected direction (incoming/outgoing/both),
preserves distinct parallel relations, and returns ``elements``, ``relations``,
``revision`` and ``truncated``. Empty kind filters permit all relation kinds.
Every returned relation includes its endpoint closure, including the endpoints
of a referenced relation. Closure records count against the object bound but
do not add traversal depth. Model ownership, process occurrence order and
non-endpoint references remain available in the returned records and complete
model; they are not silently treated as additional graph edges.
Use 1–100 roots, depth 0–4, and 1–1000 total returned objects. The cap must fit
the roots and their endpoint closure. A root closure that cannot fit is an
error; further omitted edges set ``truncated: true``. A missing root is 422.
Deterministic UUID iteration makes repeated queries of one snapshot stable.
Transactions
------------
The ``commands`` array contains the shared externally tagged Serde command
representation. It is not RFC 6902 JSON Patch. For example::
{
"commands": [
{"Rename": ["1b37b9d7-9a95-4a51-8707-010878a82419", "Assess assistance"]},
{"Describe": ["1b37b9d7-9a95-4a51-8707-010878a82419", "Review the request."]}
],
"dry_run": true
}
One-field variants use ``{"Delete":"uuid"}``; tuple variants use arrays;
named-field variants use objects. For a new typed OA activity::
{
"CreateElement": {
"id": "1b37b9d7-9a95-4a51-8707-010878a82419",
"alias": "assess",
"name": "Assess assistance",
"description": "Review the request.",
"parent": null,
"body": {"Oa": "Activity"}
}
}
``CreateArtifact``/``EditArtifact`` and ``CreateTrace``/``EditTrace`` use the same
transaction boundary as engineering edits. Artifact URI, revision and fragment
strings are opaque references. No URI is fetched or verified by the service.
Model references and alias uniqueness are checked by the core.
At most 200 commands are allowed, counting nested Batch wrappers. Explicit
creation identities allow later commands in the batch to reference earlier
creations. The shared ``execute_batch`` applies all operations to one candidate
and validates the final state. A rejected operation or final structural error
rejects the whole request. Reference rewrites/deletion cleanup follow the same
core logic as desktop editing.
Dry run still requires ``If-Match``. Its response includes ``revision``,
``dry_run``, ``changed``, ``issues`` and the proposed ``model``. It neither writes
nor reserves IDs. A committed response has the same fields except the model is
omitted. Commit must check the revision again. A no-op is successful with
``changed: false`` and the original ETag.
Contextual process and chain topology
-------------------------------------
Operational processes and system functional chains can combine work
contributions, exchange contributions, explicit controls and references to
reusable sequences. Each contribution is an ordinary ``Occurrence`` element
owned directly by the process/chain, with its stable ID in that owner's
``steps`` list. ``OccurrenceBody`` includes, for example::
{"Action":{"action":"<work UUID>","lifeline":null}}
{"Control":{"kind":"Choice"}}
{"SequenceReference":{"sequence":"<process or chain UUID>"}}
``ControlKind`` is ``Choice``, ``Merge``, ``Fork``, ``Join`` or ``Loop``.
``RelationBody.SequenceLink`` connects contribution IDs in one owner and
contains ``source``, ``target`` and a string ``guard``. Conditions belong to
choice/loop paths; an explicit ``else`` guard can describe the other path.
The readable order of the ``steps`` list remains persistent, while links
express the explicit alternative, parallel and loop topology. The core checks
owner context, work/exchange endpoint agreement and recursive sequence reuse;
engineering diagnostics identify incomplete branch cardinality or paths.
Old exchange-only sequences without explicit links retain their linear meaning.
These records use the same create/edit transaction commands and source/model
replacement routes as other engineering records. For example,
``GET /api/v1/projects/{id}/relations?kind=SequenceLink`` queries the links.
The source representation uses ``sequence @alias`` references. The service
does not execute conditions or simulate a process.
Typed scenarios and message values
----------------------------------
Scenario bodies store an explicit ``viewpoint``. Occurrence Exchange bodies
store ``message_kind``, optional correlated-call ``reply_to`` and contextual
``values``. Message kinds are Asynchronous, Call, Reply, Create, Delete, or
``{"Timer":{"expression":"after 5 s"}}``. Each value record contains a
carried DataField UUID in ``field`` and its textual ``expression``; expressions
do not change the shared exchange item's type or evaluate server-side.
``ScenarioUse`` contains a referenced ``scenario`` and one-to-one lifeline
``bindings`` records with ``referenced`` and ``local`` UUIDs. The core validates
viewpoint/layer compatibility, call/reply order and reversal, lifetime order,
carried fields, binding identity and recursive reuse through the same candidate
boundary used by the desktop. The normative details are in ``language.rst``.
Existing schema-2 files may omit the new fields: missing viewpoint maps to
MixedLegacy and missing message fields to Asynchronous, null and []. Source
exports make those compatibility defaults explicit. This preserves original V2
models while the release schema is being integrated; it does not silently
reinterpret old mixed lifelines as a strict new viewpoint.
Validation
----------
GET returns current-model diagnostics. POST accepts either::
{"format":"json", "model": {"...":"complete native Project record"}}
or::
{"format":"arcadia", "source":"complete source document"}
A well-formed validation request returns 200 with ``valid`` and ``issues`` even
if its candidate is invalid. A malformed request envelope or JSON model record
is a 400. Source diagnostics include one-based line and column according to the
shared language parser. Candidate validation does not replace current state.
Typed state machines and events
-------------------------------
OA and SA Event records use the same ``SharedBody::Event`` and ``EventBody``
contract as source and graphical editing. Signal carries same-layer item
references; Time specifies After/At and an expression; Change specifies a
condition expression. State vertices include Choice, Junction, Fork, Join,
ShallowHistory, DeepHistory, EntryPoint, ExitPoint and Terminate, as well as
State, Mode, Initial and Final. Query these through the ordinary ``kind``
filters. No timer or executable state-machine service is implied.
StateTransition keeps ``trigger``, ``guard`` and ``effect`` prose and adds
``transition_kind`` (External/Local/Internal), ``triggers`` (Event or work UUIDs)
and ``effects`` (work UUIDs). The reference lists use UUIDs in JSON and aliases
in source. Missing new properties in staged schema-2 input hydrate as
External/[]/[] without changing prior text. These defaults are compatibility
behavior pending the distinct release schema, not a versionless expansion of
every future input contract.
Use one ordinary transaction to create an event and edit its referring
transition. Reidentification updates references, and deletion prunes signal
items and effect references. Removing the last typed trigger with no prior
trigger prose leaves a descriptive replacement reminder; it does not silently
introduce a completion transition. Invalid typed references reject the whole
candidate and preserve revision/ETag. All writes retain the same conditional
commit and disk persistence contract as other semantic records.
Cross-layer transitions
-----------------------
Preview accepts the shared ``TransitionRequest`` record, including direction
(``OaToSa`` or ``SaToOa``), sources, dependency scope, mappings, selected fields,
conflict resolutions and optional destination diagram. All fields are explicit
in OpenAPI. At most 100 sources, 200 mappings and 400 field resolutions are
accepted. A conflict-free plan must fit the same 200-command transaction limit
and model-capacity limits.
The preview response is ``{"base_revision":1,"plan":{...}}``. It contains
planned changes, conflicts, warnings and exact commands/IDs. It does not write
the model or persist a server-side plan. Apply accepts that entire response and
requires the original ETag in ``If-Match``. It checks both base_revision and the
core's snapshot fingerprint before executing the shared transition validator.
Stale revisions fail with 412 and unresolved conflicts with 422. Successful
apply increments the revision once, preserving the planned identities.
A supplied plan is a client-owned command proposal. It is not cryptographically
authenticated as an untouched server preview; the same authenticated client
can submit ordinary typed commands. The core fingerprint is a change detector,
not a security digest. There is no plan token, expiration store or receipt.
Error contract
--------------
All errors have ``Content-Type: application/problem+json``. Example::
{
"type":"urn:arcadia:problem:revision_conflict",
"title":"Precondition Failed",
"status":412,
"code":"revision_conflict",
"detail":"Project changed. Reload and review the current model before retrying.",
"current_revision":3
}
``issues`` contain severity, code and message, and may include field,
object_id, line and column. Use
``code`` for client decisions, not the English message. ``X-Request-ID`` is a
response correlation UUID. This is not an idempotency key or receipt address.
* 400: malformed JSON/envelope/query/UUID or unsupported ETag form.
* 401: missing or incorrect configured bearer token; includes WWW-Authenticate.
* 403: browser Origin is unsupported, or unauthenticated Host is not loopback.
* 404: missing project or route.
* 405: unsupported HTTP method.
* 409: requested query revision changed or revision counter exhausted.
* 412: stale write precondition.
* 413: body or serialized model exceeds size limit.
* 415: unsupported Content-Type or Content-Encoding.
* 422: rejected source/model/command, model count limit, or missing graph root.
* 428: missing If-Match.
* 503: work slots full or filesystem/storage unavailable; Retry-After is present.
* 504: request deadline; an admitted write outcome may be unknown.
* 507: fixed store cap of 128 models or 256 MiB serialized envelopes reached.
* 500: unexpected server/worker failure.
Deployment and persistence limitations are documented in ``server.md``.
+68
View File
@@ -0,0 +1,68 @@
# Architecture and extension guide
The normative contract is [language.rst](language.rst); [scope.md](scope.md) explains deliberate differences from the complete Arcadia/Capella ecosystem. V2 uses one typed model and one validated mutation boundary across the desktop, text editor and REST service.
## Crate boundaries
| Crate | Responsibility | Does not depend on |
|---|---|---|
| `oa-core` | Model, structural/completeness rules, commands/history, reference maintenance, diagram projection, OA/SA transitions, text and file persistence | Iced, renderer, HTTP or native UI |
| `oa-desktop` | Iced events/state, property and source drafts, graphical views, file interactions | Server availability or an account |
| `oa-server` | HTTP contract, authentication/admission, resource revisions and atomic current-model storage | Iced or a desktop session |
The server reuses the same core library as the desktop. A new automation endpoint must produce a core command/candidate rather than implement a second version of domain rules. Desktop and server are separate storage clients: there is currently no desktop HTTP adapter. Canonical source/JSON import/export is the explicit integration path.
## Model, identity and references
`model.rs` defines Project maps for typed Elements, Relations, Diagrams, Traces and ArtifactLinks. Each diagram owns NodePlacements. All records share one UUID/alias namespace. UUIDs are durable reference identity; stored aliases are stable text labels; display names can change independently or repeat.
Element kind and layer derive from closed OA/SA bodies. Shared typed bodies represent data, interfaces, scenarios, state machines and constraints; payload variants cannot disagree with an independent kind tag. Relations are separate typed records, including relation-to-relation exchange allocation. Sequence occurrences have their own element identity; repeating an exchange uses distinct occurrences. Semantic parent, performer allocation and diagram placement are independent.
`references.rs` visits stored references for reidentification, deletion cleanup and transition mapping. New reference fields must join these visitors and their regression cases. Reidentification updates map keys, owned records, endpoints, occurrence lists, placement targets, traces and artifact owners atomically. Deleting a sequence does not delete its referenced semantic exchanges. Deleting an OA/SA element does not own or delete its cross-layer counterpart.
## Validated transactions and history
`session.rs` clones a candidate, applies a command or batch, validates the complete result, and adopts it only when valid and changed. `Replace(Project)` supports full source replacement. Record edits apply drafted properties together; typed operations handle reparenting, placement relocation, grouped movement, ordering and layout. A rejected batch leaves accepted state and history intact. Public builders construct candidates and fixtures; they are not a substitute for final validation.
`validation.rs` separates structural errors from incomplete-model warnings. Dangling references, wrong kinds/layers, ownership cycles, incompatible ports and invalid uniqueness reject a candidate. Missing allocations, incomplete references and coverage gaps allow continued authoring. Small predicates support parent/relationship choices; they must agree with the committed-model policy.
The desktop retains up to 100 full-model history snapshots and compares against a saved snapshot to compute dirty state. This favors straightforward correctness for modest models. The cost is proportional to model size; indexed validation, persistent collections or delta history should follow profiling, not speculative infrastructure.
## Nesting and graphical views
`projection.rs` derives visible nodes, visual parents and edges from a stored diagram's projection. Decomposition uses semantic ownership; allocation uses direct/effective leaf performers and computed parent summaries. Node positions/sizes/collapse belong to each view, so multiple diagrams can represent the same semantic element independently.
`diagram.rs` uses the shared projection for drawing, hit testing, Fit, grouped movement and SVG. A placement move never implicitly reparents or reallocates an element. `RelocateNode` moves a placement between diagrams while retaining its UUID, alias, geometry, collapse and references; validation rejects an incompatible destination or duplicate placement. `behavior.rs` presents sequence/scenario and state-machine content from the same typed records. UI acceptance is recorded separately from these implementation boundaries.
## Counterpart planning
`transitions.rs` builds a candidate plan from source IDs, direction, dependency scope, explicit mappings and reviewed field decisions. Participant/system-boundary choices remain explicit. Shared content and ownership references map when creating counterparts; ambiguous or incompatible mappings produce conflicts. OA-to-SA traces retain canonical endpoints regardless of which perspective starts the operation.
Existing counterparts receive reviewed name/description changes, not structural synchronization. Per-trace baselines record those reviewed values, including intentional divergence. Ordinary editing never propagates automatically. Apply rejects a stale project fingerprint or unresolved conflict, then uses the same batch validator. Desktop application creates one history entry; the service additionally checks resource revision/ETag before committing. The fingerprint is a deterministic change token, not a security signature.
Keep domain mapping choices in the core or derive UI choices from its queries. Independent desktop match tables can drift from accepted semantics; the V2 review found and corrected this kind of mismatch for fragment construction and capability/mission transition options.
## Text and draft state
`language.rs` provides a lexer/parser, deterministic formatter, tolerant source analysis and explicit V1 adapters. It collects declaration aliases before resolving UUID references, accepts forward references, rejects unknown/duplicate fields and typed-constructor errors, and validates the complete candidate. Canonical `arcadia 2` text preserves every persistent field, alias, UUID and ordered occurrence. Comments/whitespace are lexical trivia and normalize on Apply. V1 migration preserves existing identities and derives new placement/occurrence IDs deterministically.
`forms.rs` is a closed adapter over typed records. JSON values are temporary form structure; incomplete numbers, references and escaped strings stay in drafts until decoding a typed Command. This is not an extensible runtime schema or an alternate persisted metamodel. Every added field needs a usable control and meaningful authoring coverage; a no-op serialization test alone cannot establish graphical reachability.
`app.rs` coordinates the Session, selection, draft, source buffer/history, active view/camera, workflow dialogs and file state. `views.rs` renders task navigation and inspectors; `workflow.rs` handles explicit relationship/manual-trace/counterpart choices. `highlight.rs` translates tolerant core tokens to source coloring. Source lint runs after a short debounce and ignores obsolete results. Valid Apply commits once; rejected text stays editable with located diagnostics. Text-buffer history is distinct from committed-model history.
## File and service persistence
Core persistence detects JSON by content and otherwise parses Arcadia text. `.arcadia` saves canonical text; other suffixes save strict JSON. A validated snapshot is written to a sibling temporary file, flushed and atomically installed. Normal desktop saves compare the previously read bytes to detect external changes before replacement. This is an optimistic local-file check, not a guarantee against every concurrent writer or power-loss scenario. File tasks run on blocking workers; the desktop preserves drafts and pending New/Open/Close continuations on failure or cancellation.
The service stores one `{resource_id, revision, model}` envelope per current hosted project. Service identity is immutable and independent of editable model identity. A directory lock enforces one local owner; a store mutex serializes revision checks and writes. Disk replacement precedes in-memory adoption. Authentication, body/admission limits, object/query limits and total store capacity are explicit. No distributed database, retained history, idempotency ledger or automatic artifact fetching is hidden behind this interface. See [server.md](server.md) and [api.rst](api.rst) for deployment, error and concurrency behavior.
## Extending a supported concept
1. Define the semantics, field types, references and both editing obligations in `language.rst`; choose migration behavior if the stored schema changes.
2. Update the closed core model, reference visitors, validation and applicable command/transition/projection behavior.
3. Extend parser and formatter together; verify exact round-trip, invalid-input preservation and identity/reference maintenance.
4. Expose graphical creation/editing for each field/variant, including optional values and ordering. Keep draft and modal failure paths intact.
5. Update the REST/OpenAPI contract for new command or data variants; exercise actual HTTP candidate acceptance/rejection and persistence.
6. Run focused domain/interaction tests, strict workspace checks, and native Windows/Linux workflows with visual inspection at supported sizes. Update acceptance evidence only after observing the result.
No runtime plugin ABI, general extension framework or later-layer infrastructure is present. The documented core and REST boundaries provide practical development seams without requiring speculative abstractions.
+1552
View File
File diff suppressed because it is too large Load Diff
+924
View File
@@ -0,0 +1,924 @@
Arcadia Studio OA / SA Language and Editing Contract
====================================================
:Version: 2
:Status: Normative current implementation contract; first-release integration
:Core implementation: ``crates/oa-core``
:Text suffix: ``.arcadia``
1. Scope and conformance
------------------------
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,
and MAY indicate requirements, prohibitions, and permitted choices.
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.
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
incomplete-model warning when other SA elements exist. The SA System MUST NOT
contain internal components. Functional decomposition is independent of that
structural boundary. Logical Architecture, Physical Architecture, deployment,
Capella file interchange, and executable behavioral simulation are outside
this specification. This independent language does not claim complete Arcadia,
Eclipse Capella, or SysML conformance.
An implementation conforms only when each supported field and relationship is
reachable in both graphical and textual editing, preserves identity through
save/open and undo, and uses the same structural validation. Merely displaying
a name or storing an opaque payload does not establish graphical support.
Actual verification evidence is separate from this contract.
2. One authoritative model
--------------------------
A Project stores ``schema_version`` (2), ``id``, ``alias``, ``name``,
``description``, and maps named ``elements``, ``relations``, ``diagrams``,
``artifact_links``, and ``traces``. Each map is keyed by the UUID contained in
its record. Duplicate map keys and mismatching keys/identities are errors.
All objects share one UUID and alias namespace: Project, Element, Relation,
Diagram, NodePlacement, ArtifactLink, and Trace. UUIDs are durable identity;
aliases are durable, editable source labels. Display-name changes MUST NOT
regenerate existing aliases. Different objects MAY have the same display name.
Every Element has ``id``, ``alias``, ``name``, ``description``, ``parent``
(optional element reference), and a typed ``body``. Its kind and OA/SA layer
are derived from its body. A Relation has ``id``, ``alias``, ``name``,
``description``, and a typed body; it has no independently editable kind tag
that can disagree with its payload.
Diagram placement does not determine semantic ownership. NodePlacement
records belong to diagrams and reference semantic elements. Multiple diagrams
can show the same element with independent placement and collapse state.
Within one diagram, each element appears at most once.
All stored strings MUST contain only XML 1.0 characters: U+0009, U+000A,
U+000D, U+0020–U+D7FF, U+E000–U+FFFD, or U+10000–U+10FFFF. Strings are not
trimmed or Unicode-normalized. Escaping a forbidden character does not permit
it. Coordinates and sizes are finite IEEE-754 binary32 values with absolute
value at most 1000000. Width and height MUST be positive. Signed zero and
subnormal coordinates are retained.
3. Element declarations and typed fields
----------------------------------------
Each element declaration starts with its layer and kind, for example
``oa_activity`` or ``sa_function``. Every element body accepts
``description`` (default ``""``) and ``parent`` (default ``none``). The table
lists all additional fields. ``ref`` means ``@alias``; ``ref?`` also accepts
``none``. Lists preserve order. Unknown or duplicate fields are errors.
.. list-table:: Element body field catalog
:header-rows: 1
:widths: 27 43 30
* - Declarations
- Additional fields
- Defaults
* - ``oa_entity``, ``oa_actor``, ``oa_role``, ``oa_activity``, ``oa_capability``, ``oa_mission``
- None
- Empty body
* - ``sa_system``, ``sa_actor``, ``sa_function``, ``sa_capability``, ``sa_mission``
- None
- Empty body
* - ``oa_process``, ``sa_functional_chain``
- ``steps``: occurrence refs
- ``[]``
* - ``oa_exchange_item``, ``sa_exchange_item``
- ``kind``: Information, Material, Energy, Event
- Information
* - ``oa_data_type``, ``sa_data_type``
- ``definition``: Boolean, Integer, Real, String, Record, or Enumeration constructor
- String
* - ``oa_data_field``, ``sa_data_field``
- ``type_ref``; ``minimum``: u32; ``maximum``: optional u32; ``unit``: string
- String; 1; 1; ``""``
* - ``oa_interface``, ``sa_interface``
- ``items``: exchange-item refs
- ``[]``
* - ``oa_port``, ``sa_port``
- ``direction``: In, Out, InOut; ``interface``: ref?
- InOut; none
* - ``oa_scenario``, ``sa_scenario``
- ``viewpoint``: ScenarioViewpoint; ``context``: ref?; ``precondition``, ``postcondition``: strings; ``steps``: occurrence refs
- MixedLegacy for omitted schema-2 source; none; ``""``; ``""``; ``[]``
* - ``oa_lifeline``, ``sa_lifeline``
- ``represented``: participant, work, or role ref?
- none
* - ``oa_occurrence``, ``sa_occurrence``
- ``body``: occurrence constructor; ``precondition``, ``postcondition``: strings
- Exchange with none references, Asynchronous message kind and no values; two empty strings
* - ``oa_state_machine``, ``sa_state_machine``, ``oa_region``, ``sa_region``
- None
- Empty body
* - ``oa_state``, ``sa_state``, ``oa_mode``, ``sa_mode``, ``oa_initial``, ``sa_initial``, ``oa_final``, ``sa_final``
- ``kind``: StateKind; ``entry``, ``exit``, ``do_activity``: strings
- Kind selected by declaration; three empty strings
* - ``oa_choice``, ``oa_junction``, ``oa_fork``, ``oa_join``, ``oa_shallow_history``, ``oa_deep_history``, ``oa_entry_point``, ``oa_exit_point``, ``oa_terminate`` and corresponding ``sa_`` declarations
- Same State body; StateKind is Choice, Junction, Fork, Join, ShallowHistory, DeepHistory, EntryPoint, ExitPoint or Terminate
- Kind selected by declaration; three empty strings
* - ``oa_event``, ``sa_event``
- ``body``: Signal, Time or Change constructor
- Signal with ``items []``
* - ``oa_constraint``, ``sa_constraint``
- ``expression``: string; ``constrained``: semantic record refs
- ``""``; ``[]``
The explicit state ``kind`` field MUST agree with its declaration keyword.
``u32`` is an integer from 0 through 4294967295. A DataField's maximum MUST
be at least its minimum; ``none`` means unbounded. Primitive TypeRefs are
Boolean, Integer, Real, and String. A named TypeRef uses a DataType reference.
An Enumeration contains unique string literals. For example:
.. code-block:: text
definition { Enumeration { literals ["Clear", "Fog"]; }; };
type_ref { Named @weather_classification; };
These are typed constructors with named fields, not embedded JSON documents.
Constructor field names and enum spellings are case-sensitive. When replacing
a constructor, all of its fields MUST be supplied; the containing declaration
defaults do not silently fill a different constructor's missing fields.
During schema-2 compatibility, the new Exchange fields ``message_kind``,
``reply_to`` and ``values`` are exceptions: omitted values hydrate as
Asynchronous, none and [] respectively. Canonical output writes them explicitly.
This preserves older schema-2 source/JSON without inventing call correlation or
value annotations. It is not a rule for an unspecified future schema.
.. list-table:: Occurrence body constructors
:header-rows: 1
:widths: 22 78
* - Constructor
- Required fields
* - Exchange
- ``exchange``: exchange relation ref?; ``sender``, ``receiver``: lifeline refs?; ``message_kind``: MessageKind; ``reply_to``: occurrence ref?; ``values``: MessageValue records
* - Action
- ``action``: activity/function ref?; ``lifeline``: ref?
* - Control
- ``kind``: Choice, Merge, Fork, Join, Loop
* - SequenceReference
- ``sequence``: process/functional-chain ref?
* - ScenarioUse
- ``scenario``: scenario ref?; ``bindings``: LifelineBinding records
* - State
- ``state``: state/mode ref?; ``lifeline``: ref?
* - Fragment
- ``operator``: Alternative, Parallel, Loop, Optional; ``branches``: branch occurrence refs
* - Branch
- ``guard``: string; ``occurrences``: occurrence refs
For example, ``body { Action { action @assess; lifeline @operator_line; }; };``
selects an Action occurrence. Processes and functional chains contain Exchange,
Action, Control and SequenceReference occurrences. Process/chain Actions are
contextual work contributions and MUST have ``lifeline none``. A Control or
SequenceReference MUST belong directly to a Process or FunctionalChain, not a
Scenario. Scenarios contain Exchange, Action, State, Fragment, Branch and ScenarioUse
occurrences according to their enclosing ownership rules. Each
listed occurrence belongs to its enclosing sequence or branch through
``parent``. A Branch belongs to a Fragment; the Fragment's ``branches`` list
references the branches it owns.
An exchange and an occurrence of that exchange have different identities.
Repeating an exchange requires distinct occurrence records. Repeating one
occurrence identity within an ordered list is invalid. Conditions, guards,
entry/exit/do text, and constraint expressions are descriptive strings; this
version does not parse or execute them as a programming language.
4. Containment, allocation, and relationships
---------------------------------------------
Containment MUST be acyclic and stay within one layer. Entities may contain
entities, actors, and roles. Actors are not structural containers. Activities
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
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
are root semantic records and do not acquire a machine owner when referenced.
DataFields, ports, lifelines, occurrences, Regions and all state vertices
require an owner. An unowned StateMachine can be saved as incomplete
work and produces a warning. The System cannot own internal components.
The present Studio profile does not decompose SA Actors.
All relations have a header identity, alias, display name, optional
``description`` (default ``""``), and required ``source`` and ``target``
references. Their kind is the declaration keyword:
.. list-table:: Relation catalog
:header-rows: 1
:widths: 24 49 27
* - Declaration
- Endpoint meaning
- Additional fields / defaults
* - ``allocation``
- Same-layer participant → leaf activity/function
- None
* - ``interaction``
- OA activity → OA activity
- ``items`` refs / []
* - ``functional_exchange``
- SA function → SA function
- ``source_port``, ``target_port`` / none; ``items`` / []
* - ``boundary_exchange``
- Participant-owned output-compatible port → input-compatible port
- ``items`` refs / []
* - ``capability_involvement``
- Participant, activity/function, or OA role → capability
- None
* - ``chain_involvement``
- Process/functional chain → capability
- None
* - ``role_realization``
- OA entity/actor → OA role
- None
* - ``role_assignment``
- OA role → leaf OA activity
- None
* - ``mission_capability``
- OA mission → OA capability, or SA mission → SA capability
- None
* - ``mission_involvement``
- OA entity/actor → OA mission, or SA System/Actor → SA mission
- None
* - ``capability_include``
- Including capability → included capability in one layer
- None
* - ``capability_extend``
- Extending capability → extended capability in one layer
- ``condition`` string / ""
* - ``capability_generalization``
- Specific capability → general capability in one layer
- None
* - ``sequence_link``
- Preceding contribution → following contribution in one process/chain
- ``guard`` string / ""
* - ``state_transition``
- State vertices in one machine, subject to section 4.4
- ``trigger``, ``guard``, ``effect`` strings / ""; ``transition_kind`` External; ``triggers``, ``effects`` reference lists / []
* - ``availability``
- Work, participant, capability, or process/chain → state/mode
- None
* - ``exchange_allocation``
- Boundary-exchange relation → SA functional-exchange relation
- None
* - ``interface_provision``, ``interface_requirement``
- Participant/port → provided/required interface
- None
* - ``state_action``
- State/mode → activity/function
- ``phase``: Entry, Do, Exit / Do
Relation endpoints and carried ExchangeItems stay in the same layer. Parallel
exchanges with distinct identities are permitted. Self-relations are invalid
except for a state transition that returns to its source state.
Direct Allocation has at most one performer per leaf. Role sharing is a
different relationship: several OA participants can realize a Role. A direct
allocation inconsistent with its role's realized performers produces an
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.
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.
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.
Final states have no outgoing transitions. Mixing State and Mode entries is
permitted by this Studio profile. StateAction links provide typed work
references alongside the descriptive entry/exit/do strings.
4.1. Capabilities and missions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Include means the including capability requires the included capability's
behavior. Extend means the source capability adds conditional behavior to the
target; its ``condition`` records when that behavior applies. Generalization
means a specific source capability specializes a general target capability.
Each relationship stays within OA capabilities or within SA capabilities.
Include and Generalization graphs MUST each be acyclic, including transitive
cycles. Extend does not introduce an additional cycle restriction; the ordinary
no-self rule still applies. An empty Extend condition is an engineering warning
so an incomplete capability map can be authored and saved.
MissionCapability and MissionInvolvement express need and stakeholder/system
participation. They MUST NOT create a capability parent, a performer allocation,
or a cross-layer realization. These relationships remain distinct when the
same objects appear in a diagram or share names.
4.2. Contextual process and chain topology
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A Process or FunctionalChain owns contribution occurrences with stable
identities. ``steps`` is their persistent membership and presentation order;
an identity MUST occur at most once in that list. A work contribution points to
an existing same-layer Activity or Function. Reusing the same work or exchange
requires separate occurrence identities and MUST NOT duplicate the referenced
engineering element. A SequenceReference points to another same-layer Process
or FunctionalChain of the owner's kind. Its target may be ``none`` while the
draft is incomplete. Direct and transitive recursive sequence reuse are errors.
SequenceLinks connect distinct occurrences directly owned by the same process
or chain. Their source is the predecessor and target is the successor. A link
from work to an exchange requires that work to be the referenced exchange's
source; an exchange-to-work link requires the target work to be its endpoint.
Two adjacent exchange contributions require the first exchange's target to
match the second exchange's source. A mismatch is a structural error. Other
links express contextual precedence without manufacturing a global exchange.
When links or work/control/reuse contributions exist, topology is explicit.
Reordering ``steps`` MUST NOT add, remove or reorder SequenceLinks. The editor
MUST retain the difference between alternative Choice/Merge and parallel
Fork/Join contributions. Choice/Fork expect one incoming and at least two
outgoing links; Merge/Join expect at least two incoming and one outgoing link.
Loop expects at least one incoming and two outgoing links for repetition and
exit. These cardinalities are completeness warnings, not barriers to building
an incomplete flow. Choice and Loop outgoing paths with empty guards warn;
an explicit ``else`` guard is permitted. Guards remain descriptive strings.
Validation warns about missing/ambiguous entry and exit, isolated or unreachable
contributions, contributions with no reachable exit, and splits/merges that
lack explicit controls. Topology cycles are permitted for loops; the recursive
SequenceReference prohibition is a different rule. A legacy exchange-only
sequence without explicit links retains its ordered linear meaning and
adjacent-exchange gap diagnostics. Implementations MUST NOT silently invent
topology links when loading or reordering such a sequence.
4.3. Typed scenarios and reusable behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ScenarioViewpoint is one of OperationalActivity (OA activity lifelines),
ParticipantInteraction (OA entity/actor/role lifelines), Functional (SA function
lifelines), BoundaryInterface (SA System/Actor lifelines), or MixedLegacy.
The first two are OA viewpoints; the next two are SA viewpoints. Lifeline
references MUST agree with that perspective. OperationalActivity messages use
Interactions; Functional messages use FunctionalExchanges. Participant views
may use their layer's work exchanges through effective performer allocation,
or participant boundary exchanges through the relevant ports. Corresponding
lifelines and endpoints MUST agree; a viewpoint is not a label that suppresses
reference checks.
Omitted schema-2 viewpoints hydrate as MixedLegacy in both source and JSON.
This preserves pre-extension mixed work/participant scenes without discarding
their lifelines or silently assigning a stricter meaning. MixedLegacy emits a
review warning. New graphical factories explicitly select OperationalActivity
or Functional by layer. Authors can select a strict viewpoint after reviewing
the legacy scene; canonical source then records that choice.
MessageKind is Asynchronous, Call, Reply, Create, Delete, or
``{ Timer { expression "after 5 s"; }; }``. A process/chain exchange
contribution MUST remain Asynchronous with no reply correlation. Call/reply,
creation/deletion and timer semantics belong to scenarios. Reply's ``reply_to``
references a Call occurrence in the same scenario. The call MUST precede the
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
or use an explicit reverse-direction exchange. A second reply to the same call
on that guaranteed path is an error. Missing reply correlation is a draft
warning; non-Reply messages MUST NOT have ``reply_to``.
Create identifies the receiver lifeline's creation. An earlier guaranteed use
of that lifeline is an error. Delete identifies its destruction; a later
guaranteed use is an error. Ordering checks respect branch ownership; they MUST
NOT flatten alternative branches into a false unconditional order. A Timer is
local to its sender lifeline, has no global exchange or values, and has either
no receiver or the same receiver as sender. Its nonempty expression describes
the delay/deadline; an empty expression warns. Conditions and expressions are
stored engineering annotations, not an executable simulation language.
Each MessageValue is ``{ field @data_field; expression "value expression"; }``.
The field MUST be a DataField carried by the message exchange, including fields
inside named nested records. At most one annotation for a given field is
allowed in one message. Values do not overwrite the referenced type or global
exchange item. Expression text is preserved exactly; an empty expression warns.
ScenarioUse references an existing same-layer scenario and maps its lifelines
with ``bindings [ { referenced @callee_line; local @caller_line; } ];``.
Each binding is one-to-one and references a direct lifeline of the appropriate
scenario. Bound lifelines represent the same semantic object, or an explicitly
realized operational role. Strict viewpoints MUST agree; MixedLegacy retains
the compatibility case until reviewed. Missing bindings produce warnings.
Direct/transitive recursive scenario use is invalid; repetition uses a Loop
fragment instead. ScenarioUse owns neither its referenced scenario nor its
lifelines; deleting a use does not delete the reusable definition.
4.4. State vertices, events and transition contracts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
StateKind comprises State, Mode, Initial, Final, Choice, Junction, Fork, Join,
ShallowHistory, DeepHistory, EntryPoint, ExitPoint and Terminate. Every vertex
belongs to a Region. Only State and Mode are stable states: only these may own
nested Regions, typed StateAction links, availability and scenario state
observations. Multiple sibling Regions describe orthogonal configurations.
EntryPoint and ExitPoint retain Region ownership; their boundary is that
Region's owning State/Mode or StateMachine. Their graphical symbols belong on
that boundary. This is Studio's explicit containment encoding.
Events are ordinary same-layer semantic elements, independent of diagram
placements. EventBody has exactly these constructors:
.. list-table:: Event definitions
:header-rows: 1
* - Constructor
- Fields and meaning
* - Signal
- ``items`` is a unique list of same-layer ExchangeItem references; empty is valid
* - Time
- ``kind`` is After or At; ``expression`` is descriptive time text
* - Change
- ``expression`` describes the Boolean condition becoming true
After measures relative time from activation of a transition's source state;
At describes an absolute instant. Empty Time/Change expressions are located
completeness warnings. The application does not execute expressions, fire
timers or transport signals. For example::
sa_event timeout "Recovery timeout" {
body { Time { kind After; expression "30 s"; }; };
}
sa_event recovered "Pressure recovered" {
body { Change { expression "pressure > 4 && status == \"Ready\""; }; };
}
StateTransition's ``triggers`` references Events or layer-appropriate work
(OA Activity / SA Function). Referenced work need not have the machine owner's
performer: external work can cause the observed transition. Multiple triggers
are alternatives. ``effects`` references work describing effects, without
executing it. Duplicate, missing, wrong-layer or wrong-kind references are
errors. The existing ``trigger``, ``guard`` and ``effect`` strings remain
authored prose; they MUST NOT be renamed, parsed as UUIDs or inferred by name.
With typed triggers, ``trigger`` adds descriptive information. With no typed
triggers and nonblank trigger prose, the transition has an unresolved
descriptive trigger and emits a warning. Both empty denotes completion from
a stable state or transient routing from a pseudostate. Deleting the final
typed trigger when prose is empty MUST leave an explicit descriptive reminder;
it MUST NOT silently create a completion transition. Guards/effect prose remain
unchanged. Signal payload references and typed effect references are pruned
when their targets are removed.
TransitionKind is External, Local or Internal. External leaves/re-enters the
affected configuration; a stable self-transition includes exit and entry
behavior. Internal requires the same stable source and target, preserves nested
configuration, and does not imply entry/exit. Local requires a composite stable
source and proper descendant target, preserving the source activation; an
EntryPoint also routes locally into its boundary. Local self/outside targets
are invalid. Other pseudostates route externally. Endpoints MUST share a layer
and StateMachine; crossing nested Regions within that machine is not generally
forbidden. A pseudostate self-transition is invalid.
A simple state's completion means entry/do behavior is complete. A composite
completes when its active Regions complete. Final completes its containing
Region; Terminate ends the containing machine's activation. These are modeling
meanings, not evidence of a running state-machine engine.
Missing connections/guards and unfinished initial/history paths produce
warnings, so an engineer can insert vertices before wiring them. Contradictory
upper cardinalities, forbidden directions/kinds and invalid references reject.
.. list-table:: Vertex connection constraints
:header-rows: 1
* - Vertex
- Structural restrictions
- Incomplete-model warnings
* - Initial
- At most one per Region; no incoming; at most one outgoing; no outgoing trigger or guard
- Missing initial/default entry or outgoing
* - Final / Terminate
- No outgoing or nested Region
- No incoming
* - Choice
- At most one incoming; outgoing transient alternatives
- Missing incoming, fewer than two outgoing, missing guard or else
* - Junction
- Transient routing, no implicit concurrency
- Missing side or ambiguous unguarded alternatives
* - Fork
- At most one incoming; outgoing stable targets in distinct orthogonal Regions
- Missing incoming or fewer than two outgoing
* - Join
- At most one outgoing; incoming stable sources in distinct orthogonal Regions
- Fewer than two incoming or missing outgoing
* - ShallowHistory
- At most one per Region; at most one fallback to a direct stable child of that Region; no fallback trigger/guard
- Missing fallback/default entry
* - DeepHistory
- At most one per Region; at most one fallback to a stable descendant of that Region; no fallback trigger/guard
- Missing fallback/default entry
* - EntryPoint
- Incoming from outside boundary, outgoing Local into it; at most one outgoing per destination Region
- Missing outgoing or incomplete affected-Region entry
* - ExitPoint
- Incoming from inside boundary; at most one outgoing External to outside
- Missing input/output; top-level exit is an exit endpoint, not implicit termination
Fork outputs and Join inputs MUST NOT carry guards or typed/descriptive
triggers. Their distinct orthogonal Regions are the first Regions beneath the
nearest common composite or machine, not merely each endpoint's immediate
parent. Choice/Junction routing expresses conditional alternatives. Retained
pseudostate entry/exit/do prose produces a warning and remains editable; it
does not become a typed state behavior. StateAction remains the maintained
reference for entry/do/exit work on stable states.
Other pseudostate outgoing segments MUST NOT introduce event/work triggers
or trigger prose. A Choice has at most one literal ``else`` guard; the tool
does not prove arbitrary guard expressions disjoint or exhaustive. Choice
expresses a dynamic decision after incoming effects; Junction expresses a
compound path selected by guards before traversing it. ShallowHistory remembers
the immediate state and uses default entry for deeper configuration;
DeepHistory remembers nested configuration. Fallback applies when no resumable
history exists. History is per Region, including top-level Regions in this
Studio profile; no running history state is persisted or synthesized.
5. Diagrams, artifacts, and cross-layer traceability
----------------------------------------------------
``diagram`` has name, identity, alias, description, ``layer`` (Oa by default),
and ``projection`` (Overview by default). Other projections are Decomposition,
Allocation, FunctionalDataflow, Capabilities, and Traceability. Traceability diagrams can show both layers;
other diagrams show their chosen layer.
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.
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.
``node`` has an alias and identity but no display-name string. Required fields
are ``diagram`` and ``element`` references. Optional fields are ``position``
(record with x/y, default 0/0), ``size`` (width/height, default 224/94), and
``collapsed`` (false). Node identity is independent of its semantic element.
Coordinates are absolute diagram coordinates. Placement does not alter parent,
allocation, or realization. Hiding an element from a diagram must remain
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.
``artifact`` has name, identity, alias, description, required ``owner`` ref,
``relation`` (References, Satisfies, Verifies, Implements, DerivedFrom;
default References), ``uri`` (default empty), ``revision`` (optional string),
and ``fragment`` (optional string). The two optional strings default to none.
URI must be nonempty, have no whitespace/backslash/control characters, and
use valid percent escapes. The owner is another existing model object.
Validation MUST NOT fetch the URI. Revision and fragment are retained verbatim.
``trace`` has name, identity, alias, description, required ``oa`` and ``sa``
endpoint references, ``kind`` (default Refinement), and ``baseline`` (default
none). Endpoints are canonical: SA realizes OA, regardless of which side
initiated creation. Trace does not change endpoint identity or containment.
Multiple distinct counterparts are permitted; duplicate endpoint/kind triples
are invalid.
.. list-table:: Trace kinds
:header-rows: 1
:widths: 23 77
* - Kind
- OA → SA endpoint types
* - Refinement
- Activity → Function
* - Participant
- Entity/Actor → System/Actor
* - Capability
- Operational Capability → System Capability
* - Mission
- Operational Mission → System Mission
* - Exchange
- Interaction relation → FunctionalExchange relation
* - Sequence
- Process → FunctionalChain, or paired occurrence contributions directly owned by Process / FunctionalChain
* - Item
- Same shared kind: ExchangeItem, DataType, DataField, Interface, or Port
* - Scenario
- Same shared kind: Scenario, Lifeline, or Occurrence
* - State
- Same shared kind: StateMachine, Region, any StateKind vertex, or Event
* - Manual
- Engineer-defined relationship between OA/SA elements or relations
A nonempty baseline is ``{ oa { name "..."; description "..."; };
sa { name "..."; description "..."; }; }``. It records last-reviewed names
and descriptions, not a full structural snapshot. Implementations MUST NOT
present it as structural synchronization or a lossless inverse transition.
Explicit counterpart creation, linking, comparison, and reviewed property
update may start in either layer. Ordinary edits MUST NOT automatically
overwrite the counterpart. Deletion MUST NOT silently cascade across layers.
Operational Capability normally creates a System Capability. Operational Mission
and System Mission are default counterparts in both directions and use Mission
traces. An explicit Operational Capability ↔ System Mission mapping remains
supported with a Manual trace; a uniquely linked such counterpart is reused
instead of silently creating a different mapping. Required/subtree process and
chain transitions include their contextual links, owned contributions and
referenced dependencies. New process/chain contribution mappings use Sequence
traces; older Scenario-kind occurrence mappings remain accepted. The current
name/description baseline does not reconcile later topology changes.
Name and description are compared independently against each endpoint's
last-reviewed baseline. An unchanged source MUST NOT overwrite an accepted
different target value. A source-only change proposes an update; different
changes on both sides require an explicit decision to take the source, keep
the target, or enter a merged value. An explicit decision records both current
values as reviewed, even when they differ. A target-only edit is preserved
without advancing that field's baseline, so it remains eligible to propagate
in the reverse direction. Equal current values can be recorded as converged.
6. Text grammar and resolution
------------------------------
The input is UTF-8 without a byte-order mark. LF, CR, space, and tab separate
tokens. ``//`` starts a comment through the next LF or end of input. Quoted
strings use JSON string escaping, including ``\n``, ``\r``, ``\t``, ``\\``,
and Unicode escapes. Literal unescaped newlines inside strings are invalid.
Numbers use JSON decimal/exponent syntax; NaN, infinity, hexadecimal numbers,
leading plus signs, and leading zeroes are invalid.
Aliases match ``[A-Za-z_][A-Za-z0-9_]*``. The following aliases are reserved:
arcadia, project, id, oa, sa, element, relation, diagram, node, artifact,
trace, body, alias, name, description, parent, from, to, entity, actor,
activity, capability, process, allocation, interaction, contribution,
realization, position, steps, true, false, null. Other field/constructor
spellings can be used in alias positions because references begin with ``@``.
The grammar below uses ``*`` for repetition and ``?`` for optional syntax.
``kind`` is exactly an element/relation keyword listed above. Typed field
sets and enum constructors are closed by sections 3–5.
.. code-block:: text
document = "arcadia" "2" ";" project
project = "project" alias string identity? "{" project-entry* "}"
project-entry = "description" string ";" | declaration
declaration = (kind | "diagram" | "artifact" | "trace")
alias string identity? fields
| "node" alias identity? fields
identity = "id" uuid-string
fields = "{" (field-name value ";")* "}"
value = string | number | "true" | "false" | "none"
| enum-word | "@" alias | list | fields
list = "[" (value ("," value)*)? "]"
The project description defaults to empty and may appear once. There is no
semicolon after a declaration or project closing brace. Every field inside a
record ends with a semicolon, including fields whose values are nested records.
Trailing list commas are invalid. Fields may appear in any order.
UUID strings use 8-4-4-4-12 hexadecimal spelling, with uppercase or lowercase
hexadecimal accepted. Canonical output uses lowercase. Omitting an identity
creates a fresh UUID for that parse. After Apply, canonical text includes the
generated identity. Authors must keep identity clauses to preserve existing
objects across subsequent edits.
All declaration aliases are collected before references resolve, so forward
references are legal, including references to nodes and diagrams. Unknown
``@alias`` values are errors at their reference occurrence. Quoted UUIDs and
bare words cannot substitute for reference-valued fields. Alias resolution
produces UUID references; the alias itself remains stored on its declaration.
Changing an alias in source requires updating its uses. Display names never
resolve references.
Unknown fields, duplicate fields, incompatible constructor payloads, unsupported
enum spellings, duplicate aliases/identities, and structural validation errors
reject the entire candidate. No declaration silently replaces another.
Ordinary string fields require quoted strings and cannot contain an unquoted
``@alias`` 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.
7. Canonical output and source tooling
--------------------------------------
For every structurally valid Project P, parsing its canonical output MUST
reproduce P, and canonicalizing that result MUST be idempotent:
.. code-block:: text
parse(format(P)) == P
format(parse(format(P))) == format(P)
Equality includes all persistent fields, stored aliases, body variants,
references, placements, artifacts, trace baselines, and ordered occurrences.
Map order is irrelevant. The writer preserves signed coordinate zero and
emits decimals that recover the original binary32 value.
Canonical output uses LF, four-space indentation, explicit identities,
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.
Comments and input whitespace are lexical trivia. Canonicalizing or applying
source does not preserve them; the editor MUST explain that behavior.
Camera position, selection, source cursor, and undo history are not stored
model fields. Layout and collapse state inside NodePlacement are model data.
``parse`` 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
contain severity (Error or Warning), a code identifying the diagnostic category,
optional object UUID and field path, a message, one-based line and Unicode-scalar
column, and a half-open UTF-8 byte Span. Tabs count as one scalar; LF advances
the line. Structural errors and engineering warnings point to a relevant
declaration or field when available. Clients MUST use severity/code rather than
parse the human message; diagnostic wording is not a stable machine API.
``parse_limited`` and ``decode_limited`` accept caller admission budgets for
input bytes, graph objects, total persistent records and ordered step references.
Counts MUST be checked before expensive structural validation; migration budgets
include generated records before migration. Exceeding a budget does not mutate
the model. Admission limits are host capacity policy, not hidden restrictions
on otherwise valid language semantics; ordinary ``parse`` has no such model
size policy. The REST service's fixed policy is specified in ``api.rst``.
``tokenize`` is tolerant: it covers every input byte with nonoverlapping
TokenSpans, including incomplete strings and invalid characters. Token classes
are Keyword, Identifier, String, Number, Comment, Punctuation, Whitespace,
and Error. ``@`` is punctuation followed by the referenced alias token.
``lint`` returns syntax, structural and engineering warning diagnostics for the
current draft without adopting a model. A valid unallocated SA function, for
example, can report missing System and allocation warnings before Apply.
``analyze``
combines tokens, diagnostics, and declaration spans with alias, optional
explicit UUID, and declaration kind. A malformed later declaration does not
erase earlier declaration spans. None of these APIs mutates a session.
8. Graphical parity and transactional editing
---------------------------------------------
The graphical editor MUST expose all persistent properties through typed
controls, including optional references, lists, variant constructors, aliases,
IDs, nested parent changes, diagram placements, artifact metadata, and trace
baselines. Inspector controls may provide operations unavailable as a canvas
gesture. Exact strings, including tabs and line breaks in names, must remain
editable through an appropriate escaped-string input when ordinary widgets
cannot represent them.
A move into a functional parent, an allocation onto a performer, and a diagram
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.
Applying complete source is one model transaction. Graphical edits regenerate
the matching canonical source. Both paths validate their candidate 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.
Source and property drafts contribute to unsaved-work state. Invalid drafts
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.
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
leaving dangling references. Deleting a sequence does not delete the semantic
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.
9. Persistence and V1 migration
-------------------------------
Loading detects JSON from its first non-whitespace ``{``; other input is parsed
as Arcadia source regardless of filename. Saving an ``.arcadia`` suffix uses
canonical V2 text; other suffixes use V2 JSON. Both forms preserve the same
complete model. JSON rejects unknown fields throughout typed payloads,
duplicate maps, unsupported versions, and structural errors.
During first-release schema-2 integration, old Scenario bodies without a
``viewpoint`` retain MixedLegacy, and old Exchange occurrence bodies without
``message_kind``, ``reply_to`` or ``values`` retain Asynchronous, none and [].
Old StateTransition bodies without ``transition_kind``, ``triggers`` or
``effects`` retain External, [] and []; existing ``trigger``, ``guard`` and
``effect`` strings are preserved exactly. Source declaration defaults and JSON
compatibility defaults MUST agree; no event or work reference is inferred from
legacy prose.
This compatibility applies equally to source and JSON. It adds explicit
defaults without changing any prior identity, name, reference, order, geometry,
artifact or trace field. The original V2 golden source/JSON in
``crates/oa-core/tests/fixtures`` 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.
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
and crash recovery are outside this storage contract.
Version 1 text and JSON are accepted through an explicit adapter. Migration
preserves project, element, and relation UUIDs; names and descriptions; relation
endpoints; all coordinates; and exact process exchange order and repetition.
Allocation and Interaction remain their corresponding V2 relations.
Contribution becomes CapabilityInvolvement; the old Process-to-Capability
Realization becomes ChainInvolvement, retaining its ID and name.
Legacy positions become nodes in one OA Overview diagram. Each old process
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.
No System or SA content is invented during an OA-only migration.
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.
10. Complete authoring example
------------------------------
This document intentionally omits IDs for new authoring. Apply and Save write
the assigned UUIDs explicitly.
.. code-block:: text
arcadia 2;
project harbor "Harbor assistance" {
oa_entity control "Harbor control" {}
oa_activity assess "Assess assistance" {}
allocation performs "Performs" {
source @control;
target @assess;
}
sa_system service "Harbor assistance system" {}
sa_function evaluate "Evaluate assistance request" {}
allocation system_performs "Performs" {
source @service;
target @evaluate;
}
trace assessment_trace "Operational realization" {
oa @assess;
sa @evaluate;
kind Refinement;
}
diagram operational_view "Operational allocation" {
layer Oa;
projection Allocation;
}
node assessment_card {
diagram @operational_view;
element @assess;
position { x 360; y 70; };
}
}
11. Verification and primary references
---------------------------------------
``cargo test -p oa-core --test language --locked`` verifies typed round-trips,
stable aliases, strict syntax/reference failures, Unicode source spans,
deterministic V1 migration, occurrence identity, coordinate bits, persistence,
and model-history integration. Core validation and session tests cover domain
mutation rules; native UI and API verification are separate evidence.
The research record in ``docs/research-v2-semantics.md`` distinguishes method
requirements from Studio design choices and links to original UI evidence.
The following sources inform this language without extending its scope:
* `Arcadia Q&A <https://mbse-capella.org/arcadia-qna.html>`_
* `Arcadia language reference <https://mbse-capella.org/resources/arcadia-reference/Arcadia%20Language%20-%20MetaModel.pdf>`_
* `Capella allocation guide <https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.ui.doc/html/05.%20Diagram%20Management/Allocating%20Functions%20on%20Components.mediawiki>`_
* `Capella iterative transitions <https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.transitions.doc/html/11.%20Iterative%20Transitions/11.1.%20Concepts.mediawiki>`_
* `W3C XML 1.0 characters <https://www.w3.org/TR/xml/#charsets>`_
+5444
View File
File diff suppressed because it is too large Load Diff
+322
View File
@@ -0,0 +1,322 @@
# V2 architecture research and proposed extension
Status: implementation proposal, 2026-09-05. This document maps the current source and recommends a concrete OA/SA extension. It does not certify that V2 behavior exists, change the current normative language contract, or claim Capella interchange/conformance. No source code, harness, builds, or GUI sessions were changed for this investigation.
The investigation began with `harness/STATE.md`, `harness/PLAN.md`, `harness/DECISIONS.md`, the architecture review, README, and `docs/{scope,architecture,acceptance,validation}.md`, followed by the normative `docs/language.rst` and core/desktop implementation and tests. Domain names below describe a proposed bounded dialect; the accompanying method research should settle precise OA/SA terminology before the V2 specification is frozen.
## Recommended shape
Keep one typed Rust project and one validated transaction boundary. Extend the existing core with layer-specific records, explicit semantic containment, separate diagram placements, stable stored aliases, artifact links, and first-class OA/SA traces. Both editors and a small REST service must use the same commands, transition planner, source analyzer, and validator.
The smallest useful implementation is an extension of the current two-crate design plus a server adapter. It does not need a second metamodel, automatic continuous synchronization between layers, a general-purpose graph database, a runtime metamodel/plugin framework, or a physical deployment model disguised as System Analysis. Larger modules can be split when the concrete records below arrive.
## Current implementation and concrete fault lines
| Existing seam | What is implemented | What V2 must change |
|---|---|---|
| `crates/oa-core/src/model.rs:11` | Five closed OA element kinds; layer is implicit. | Use an explicit OA/SA kind distinction so a System Function cannot accidentally become an Operational Activity with an arbitrary layer tag. |
| `model.rs:54` | Every element has one absolute position and a `steps` vector, including kinds that cannot use steps. | Separate semantic ownership from placements; move process/chain steps into typed bodies. |
| `model.rs:78` | Relation admissibility is a predicate of two kinds only. | Check layer, ownership, endpoint roles, allocation cardinality, and any exchange/port contract in shared semantic functions. |
| `model.rs:118` | Project has only two UUID maps; strict decoding rejects unknown fields. | Introduce a schema-2 DTO and explicit schema-1 migration. New fields cannot simply be added and expected to load old JSON. |
| `session.rs:33,40` | `Session.project` is publicly mutable and `Session::new` trusts its caller. | Make accepted state private; `try_new` validates. This becomes necessary once REST and multiple frontends are callers. |
| `session.rs:70` | Clone, mutate, validate, adopt, and snapshot history form one atomic command. | Preserve this behavior; add batch preparation/commit, structured results, revision preconditions, and transition plans. |
| `session.rs:148,203` | Delete knows incident relations and process steps; reidentification knows endpoints and process-step IDs. | Cover containment, role realization, exchange items, lifelines, occurrences, state machines, traces, artifacts, and placements through one explicit reference visitor/index. |
| `session.rs:170` | Arrange groups every project element into five global kind columns. | Layout the active view and visible hierarchy; moving/arranging must not reposition a different layer or hidden diagram. |
| `validation.rs:13` | Structural errors and incomplete-model warnings share one pass. Allocation is direct and OA-only; process continuity compares adjacent exchange endpoints. | Add stable diagnostic codes/object/field paths, layer-aware rules and hierarchy-aware warnings. Preserve repeated process occurrences and warning-only incomplete models. |
| `language.rs:39,109` | Printer regenerates aliases from names in UUID-map order, with collision suffixes. | Persist aliases. A rename currently rewrites references and can renumber unrelated same-name aliases; a smaller UUID insertion can also change collision assignments. |
| `language.rs:27,203,401` | Lexer/parser are private; parsing usually returns the first error, source locations are starts only, comments are dropped. | Expose tolerant token spans and a noncommitting analysis result for highlighting/linting; keep strict Apply separate. |
| `persistence.rs:15,47` | Content-based text/JSON loading, shared validation, sibling-temp atomic save. | Version dispatch, deterministic migration, read/write conflict fingerprints, and server repository commit semantics. Atomic replacement alone does not detect external modifications. |
| `oa-desktop/src/app.rs:614,769,846,853,959` | One selection ID, one property draft, one source buffer, one camera; accepted commands regenerate canonical text. | Preserve single-draft ownership, add typed selection/context and source baseline revisions, and use command-result remaps instead of selected-ID-specific fixups. |
| `app.rs:1044`, `views.rs:617` | Pickers filter kinds; inspector handles only project/element/relation. | Use core candidate queries for legal parents/endpoints/allocations; add graphical controls for every new mutable record field. |
| `views.rs:329` | Monospaced text editor and Apply diagnostics; no highlighting or live linting. | Highlight the actual language and display debounced diagnostics without committing the draft. |
| `diagram.rs:10,31,322`, `app.rs:1532` | Fixed-size flat cards, reverse-map hit testing, global Fit and global SVG export. | Resolve active-view bounds, paint parents before children, hit-test deepest visible nodes, and share a view projection between canvas, Fit, and SVG. |
The existing test suite supplies valuable foundations: exact parse/print equality and canonical idempotence, exact strings and floating-point coordinates, parallel interactions, repeated ordered steps, rejected-edit history preservation, identity remapping, strict JSON, atomic saves, and desktop draft/identity guards. The repository records 50 tests in the completed MVP; this investigation read the tests and did not rerun them. None of that historical evidence demonstrates V2 behavior.
## Typed schema-2 records
Retain UUID identity and ordered maps. Avoid opaque `properties: Map<String, Value>` as the representation of new semantics. The following is an API sketch, not compilable Rust or a finalized JSON grammar:
```rust
enum Layer { Operational, System }
enum ElementKind { Operational(OaKind), System(SaKind) }
enum OaKind { Entity, Actor, Role, Activity, Capability, Process }
enum SaKind { System, Actor, Function, Capability, Mission, FunctionalChain }
struct Header { id: Id, alias: Alias, name: String, description: String }
struct Element {
header: Header,
parent: Option<Id>, // semantic decomposition, not performer
body: ElementBody, // kind derived from this closed enum
}
enum ElementBody {
OperationalEntity, OperationalActor, OperationalRole,
OperationalActivity, OperationalCapability,
OperationalProcess { steps: Vec<ExchangeOccurrence> },
System, SystemActor, SystemFunction, SystemCapability, SystemMission,
FunctionalChain { steps: Vec<ExchangeOccurrence> },
}
struct ExchangeOccurrence { id: Id, alias: Alias, exchange: Id }
struct Relation { header: Header, body: RelationBody }
struct Project {
schema_version: u32,
header: Header,
elements: BTreeMap<Id, Element>,
relations: BTreeMap<Id, Relation>,
exchange_items: BTreeMap<Id, ExchangeItem>,
scenarios: BTreeMap<Id, Scenario>,
state_machines: BTreeMap<Id, StateMachine>,
traces: BTreeMap<Id, Trace>,
artifact_links: BTreeMap<Id, ArtifactLink>,
diagrams: BTreeMap<Id, Diagram>,
}
```
`ElementBody` is the source of its kind and layer. Do not store both `kind` and an independent contradictory body/layer. Existing method calls can expose `kind()` and `layer()` so UI matches remain exhaustive. An SA project may be incomplete: no system yet is a warning; more than one system of interest in the project's single SA analysis is an error. SA actors are outside that system boundary. Do not introduce logical components, physical nodes, or deployment as SA kinds.
Use `ObjectRef` with explicit variants for project, element, relation, exchange item, scenario, occurrence, state machine/state/transition, trace, and diagram as needed. IDs still occupy one global namespace, including occurrences and diagram nodes; references also check their expected category. This makes artifact targets and diagnostic focus precise without making all ordinary relations accept arbitrary endpoints. Plain typed helpers (`visit_refs`, `remap_refs`, `dependencies`) suffice; a generalized runtime reflection system is unnecessary.
### Containment and allocation
Persist one `parent` reference per semantic element, with children derived. Do not persist both children lists and parent pointers. Canonical declarations remain flat with `parent alias;`, so moving a subtree changes one ownership line rather than indenting and relocating every descendant. The graphical explorer/canvas still presents a hierarchy.
Recommended initial containment matrix:
| Child | Allowed semantic parent |
|---|---|
| OA entity | OA entity, or layer root |
| OA actor | OA entity, or layer root; actor remains a distinct participant kind |
| OA activity | OA activity, or layer root |
| OA capability | OA capability, or layer root |
| OA role/process | Layer root initially; involvement is represented by relations |
| SA function | SA function, or layer root |
| SA capability | SA capability, or layer root |
| SA system/actor/mission/chain | Layer root initially |
These are deliberately explicit product rules to reconcile with the method research. Adding an allowed parent is a small validator/spec change. Allowing every kind under every kind would lose meaningful validation. Cross-layer parents, self-parenting, multiple ownership, and cycles reject a transaction. Empty roots and incomplete allocations remain editable. Neither graphical overlap nor dragging inside a card silently changes semantic parent or performer: a dedicated nest/reparent/allocate action previews the semantic edit.
Allocation is independent of decomposition. An activity may be decomposed into activities performed by different operational participants. A function's decomposition is independent of whether the system or an external actor performs it. Persist direct allocations only. The first release should not infer or materialize allocation inheritance: a parent allocation does not silently allocate descendants. Diagnose unallocated leaves; label any aggregate coverage as derived. If later requirements demand inheritance, define effective-performer resolution and explicit override rules before changing this behavior.
Keep at most one direct performer per OA activity and SA function. OA roles organize responsibility through explicit participant-to-role realization and role-to-activity assignment; validate that resolving an assigned role does not create contradictory direct performers. A role may be unfilled or filled by multiple participants as a warning/incomplete modeling state only if the chosen dialect permits it; do not silently choose the first participant. The simplest initial policy is to keep role assignment separate from the existing authoritative direct allocation and report any mismatch. This preserves schema-1 meaning and avoids inventing implicit execution semantics.
### Layer-specific relationships
Use typed `RelationBody` variants with meaningful field names, even when several variants are stored in the same map. Each variant defines its live references, layer rule, and allowed endpoint query in core.
| Relationship family | Endpoints and principal constraints |
|---|---|
| OA allocation | OA entity/actor → OA activity; one direct performer per activity |
| OA interaction | OA activity → OA activity; same layer, distinct endpoints; parallel distinct-ID exchanges remain legal |
| OA contribution / process realization | Preserve existing activity → capability and process → capability semantics; label these as the existing dialect's OA support links |
| Role realization / role assignment | OA entity/actor → OA role; OA role → OA activity; independent of containment |
| Capability involvement | OA participant → OA capability, or SA system/actor → SA capability; never an accidental cross-layer ordinary relation |
| SA allocation | SA system/actor → SA function; one direct performer per function |
| SA functional exchange | SA function → SA function; same layer, distinct endpoints; carries exchange-item references |
| SA capability support | SA function/functional chain → SA capability; distinct variant from an OA↔SA trace |
| SA mission capability link | SA mission → SA capability; exact label should follow the method vocabulary adopted for V2 |
| Exchange item carriage | OA interaction or SA functional exchange → same-layer exchange item; no deduplication that merges parallel exchanges |
| State/mode availability | Same-layer element → state/mode belonging to a relevant context; stored as an explicit typed association |
Use distinct names for process-to-capability realization, function-to-activity traceability, and external requirement satisfaction. The existing `Realization` enum must not become a catch-all for unrelated semantics. Direction and admissibility must be testable without GUI code.
### Practical behavioral records
Add the requested practical fields as explicit records rather than free-form descriptions:
* `ExchangeItem { header, layer, item_kind, fields: Vec<ItemField> }`, with stable field IDs/aliases, field name, a bounded type reference (primitive or another item), multiplicity bounds, and unit/description where needed. Use cases include information and material; do not fabricate a physical communication protocol. Validate field references, multiplicity, and item layer. A self-referential data type can be permitted explicitly without confusing it with forbidden containment cycles.
* `Scenario { header, layer, context: ObjectRef, lifelines, steps }`. Lifelines have stable IDs and reference same-layer participants or supported behavior elements. Each ordered occurrence has its own ID; exchange occurrences identify the exchange and sending/receiving lifelines; action occurrences identify the activity/function and lifeline; state observations reference a defined mode/state. Repeated exchanges remain separate occurrences. Bounded `Sequence`, `Alternative`, `Optional`, `Loop`, and `Parallel` fragments contain ordered branches, each with a guard and child occurrences. Guards/events remain expressions or text in a specified limited language; there is no claim of executable simulation merely because a scenario is drawn.
* `StateMachine { header, layer, owner, states, transitions }`. State/mode records have stable IDs, an explicit `State` versus `Mode` kind, parent/region membership, initial marker and entry/exit/do text. Transitions have source, target, trigger, guard, and effect. Start with hierarchical exclusive regions: at most one initial child per region, absence is a warning, cross-machine endpoints are errors. Orthogonal regions and executable guard semantics require an explicit later contract rather than pretending arbitrary nesting implements them.
Scenario ordering and state-machine transitions are different structures. An ordered OA process/SA chain remains a lightweight exchange sequence and can coexist with richer scenarios. Do not coerce every scenario into the legacy `Element.steps` vector. GUI editors must be able to create/reorder repeated occurrences and edit all fragment/branch/guard, lifeline, state, and transition fields that source accepts.
### Diagram records
Introduce at least independent OA and SA views and a mixed traceability view. The smallest concrete record is `Diagram { header, kind, nodes: BTreeMap<Id, NodePlacement> }`, with `NodePlacement { id, alias, element: Id, position: Position, size: Size, collapsed: bool }`. Initially permit at most one placement per element per diagram. The diagram kind is a closed enum naming its layer and projection (overview, decomposition, allocation, or mixed traceability); a second independently contradictory layer flag is unnecessary. Edge endpoints/routes are derived, so no edge-layout map is necessary until route editing is actually supported. The same element may appear in different views without duplicated semantics. Keep positions absolute in diagram coordinates initially to preserve exact existing values and avoid coordinate-frame ambiguity.
A decomposition presentation derives visible container relationships from semantic parents; an allocation presentation derives performer/activity or system/function grouping from allocation relations. These are different projections of the same records, with fixed documented rules per diagram kind. No visual parent pointer is persisted. Moving a visible container translates its visible descendants in that view in one transaction. Collapsing hides descendants without deleting semantic records. Exact X/Y/width/height and collapse controls are available in the inspector and text. Fit and SVG use the same visible projection and container bounds as the canvas. Selection should distinguish semantic object and diagram occurrence, so changing an element's name affects all views while moving a node affects its selected view. Arbitrary grouping, editable routing, and a plugin view framework can wait.
## Artifact links without vendor coupling
Use a first-class record:
```rust
struct ArtifactLink {
header: Header,
owner: ObjectRef,
relation: ArtifactRelation, // references, satisfies, verifies, implements,
// derived_from, or explicitly labeled custom
uri: String,
revision: Option<String>,
fragment: Option<String>,
rationale: String,
}
```
Store the exact URI, revision, and fragment supplied by the user. A revision is an opaque external version/commit/tag, not a project revision number or a Git-only SHA. Support arbitrary well-formed URI schemes and documented relative URI references resolved against project location. No HTTP-only restriction, Windows path coercion, lowercasing, eager dereferencing, or URL normalization should change identity. A native path may be accepted by a UI convenience action and converted explicitly to a `file:` URI.
The validator checks syntax, text limits, and internal owner existence; external reachability is an optional nonblocking diagnostic. Loading, linting, previewing, and displaying a model must not fetch links. Opening a supported URI is a separate user action; unfamiliar schemes can remain copyable even if no opener is registered. This supports requirement tools, source repositories, documents, CAD/data artifacts, and proprietary systems without baking a provider into the metamodel.
## Bidirectional transitions and traceability
A transition is an explicit modeling operation that can create or update selected counterpart records in either direction. It does not change an existing object's layer/kind in place or imply that OA and SA are identical. Operational activities can be refined into several functions, and several operational concepts can support one system concept.
Store traces canonically as `oa: ObjectRef` and `sa: ObjectRef`, independent of whether the user initiated OA→SA or SA→OA. A trace includes ID/alias/name, a typed mapping kind, rationale, and a last-reviewed synchronization baseline. Endpoint-kind rules allow appropriate element pairs, exchange pairs, process/chain pairs, item pairs, and scenario pairs. A generic explanatory trace may connect other supported pairs but does not make them eligible for automatic copying. Many-to-many semantic traceability is valid; duplicate identical mapping links should be rejected or explicitly distinguished by purpose.
Minimum transition mappings:
| Source/target pair | Creation and update behavior |
|---|---|
| OA activity ↔ SA function | Copy selected name/description fields; create/reuse counterpart and trace. Decomposition can be included using an explicit parent mapping. |
| OA participant ↔ SA actor or system | OA→SA requires the user to select system-of-interest versus external-actor treatment. Reverse mapping requires a chosen OA entity/actor kind; do not guess from labels. |
| OA capability ↔ SA capability | Copy selected descriptive fields and create/reuse trace; support/involvement relations require mapped endpoints. |
| OA interaction ↔ SA functional exchange | Require or explicitly create both endpoint mappings; items map by selected item traces; preserve parallel exchange identities. |
| OA process ↔ SA functional chain | Map each ordered occurrence, preserving order and repetition; unresolved exchange mappings block that proposed sequence, not the entire ability to model. |
| OA exchange item ↔ SA exchange item | Copy selected fields through an explicit item/type mapping; report unsupported types rather than dropping them. |
| OA scenario ↔ SA scenario | Map context, lifelines and occurrences/fragments through existing/selected traces; unresolved participants or behavior are shown as plan conflicts. |
| Modes/states and roles | Trace and manual mapping are always possible where valid. Automatic conversion needs an explicit mapping rule; OA roles do not automatically become SA components. |
### Preview/commit algorithm
1. `plan_transition(project, request)` reads an accepted snapshot and selected source IDs, direction, mapping choices, fields to copy, dependency scope, and requested destination view. It never mutates the active model.
2. Resolve eligible traces first. For one-to-many matches, require explicit selected targets or create-new choice. Display create, reuse, update, skipped, and conflicted records separately; never match solely by name.
3. Expand only the chosen dependency closure. For example, an exchange needs mapped endpoints, and a chain needs its mapped exchanges. Do not silently migrate the entire model or relocate an existing counterpart's semantic parent.
4. Produce proposed typed commands with new IDs/aliases generated once, source/destination field changes, trace creation/updates, unresolved choices, and structural diagnostics. Preview includes the base revision and a plan fingerprint. Retry uses the same planned IDs; apply does not regenerate a fresh duplicate model.
5. Commit verifies the base revision and accepted plan fingerprint, revalidates the complete candidate, and adopts the entire batch as one history operation. Any unresolved error, stale plan, or persistence failure rejects without changing accepted state. A repeated successful API request with the same idempotency key returns its original result.
6. Show both layers and their traces after commit. Reverse transition uses the same planner and canonical trace orientation. Repeated planning of an unchanged, already-linked projection proposes no duplicate creation or meaningless history entry.
### Drift and conflicts
Persist the last explicitly accepted pair of synchronization projections on an eligible trace. Projections contain only mapped semantic fields and typed references; aliases, layout, timestamps, and unrelated properties do not cause propagation. Baselines are historical values, not extra live domain objects. They are serialized, inspectable in text and GUI, and updated only by accepting a transition/reconciliation operation. A copied status flag alone cannot detect drift.
Compare current OA and SA projections against their respective baselines. If neither changed, there is no update. A source-only change can propose copying the selected fields. A destination change, changes on both sides, missing baseline, changed mapping cardinality, or unsupported conversion requires a review choice per affected field/relationship. Present preserve destination, take proposed source, or supply an explicit merged value. Do not silently overwrite the destination because its trace happens to exist. A coarse pair of hashes is sufficient for conservative whole-object conflicts, but structured baseline fields enable a useful property diff and safe independent-field merging.
One-to-many mappings do not imply broadcasting names, deleting surplus targets, or reversing a decomposition into a flat record. A transition may offer such changes only as visible, selected plan operations. Deleting an endpoint requires an impact plan for its traces; it must not automatically delete a counterpart in another layer. Removing a trace simply unlinks the objects. Reidentification rewrites live references and uses an explicit remap policy for baseline references; historical values must not masquerade as dangling live references.
## Shared command and API boundary
Extend `Session` incrementally rather than placing transition semantics in `app.rs`:
```rust
struct CommandEnvelope {
expected_revision: Revision,
request_id: RequestId,
commands: Vec<Command>,
}
struct ChangeResult {
revision: Revision,
created: Vec<ObjectRef>,
changed: Vec<ObjectRef>,
deleted: Vec<ObjectRef>,
identity_remap: BTreeMap<Id, Id>,
diagnostics: Vec<Diagnostic>,
}
// prepare applies to a candidate; commit adopts only that verified candidate.
fn prepare(&self, envelope: CommandEnvelope) -> Result<PreparedChange, Errors>;
fn commit(&mut self, prepared: PreparedChange) -> Result<ChangeResult, Conflict>;
```
Add concrete create/edit commands for new records, `Reparent`, `Allocate`, occurrence insert/reorder/remove, diagram placement changes, trace/artifact CRUD, `Replace`, and transition-plan application. Whole-document Replace is still one candidate transaction. Use IDs for ordered occurrences; stale remote indices should not accidentally reorder a different step. Structural checks occur after the whole batch, so creating a child and parent or remapping all relations does not require a temporarily valid intermediate model.
Make session state private and validate every accepted entry point. Keep revision and runtime history outside the persistent model to avoid one Git diff line changing on every command. An accepted revision changes for undo/redo too. Undo/redo remain session-local committed model operations; a shared server must not let one client unknowingly undo another client's work. The initial backend can expose one coordinated writer/session with revision checks instead of claiming collaborative multi-user undo.
Deletion needs a core impact query. Offer explicit subtree-delete versus reparent-children behavior, show removed incident relations/occurrences/traces/artifact links/view nodes, and preserve other-layer counterparts. Both GUI and REST submit the selected policy. Text replacement naturally expresses the final candidate but receives the same dangling-reference errors. One shared reference traversal powers impact, reidentify, validation, trace queries, and test fixtures.
### REST adapter
Add `crates/oa-server` around core/application services. Desktop defaults to in-process calls; when connected to a server it uses that server's accepted model and revisions rather than independently mutating a duplicate authoritative session. Core must not depend on HTTP, Iced, sockets, dialogs, or provider APIs.
Suggested initial routes:
| Route | Contract |
|---|---|
| `GET /v1/projects/{id}` | Schema-2 snapshot and ETag/current revision |
| `GET /v1/projects/{id}/source` | Canonical `.arcadia` representation and same accepted revision |
| `POST /v1/analyze` | Noncommitting text analysis, tokens/diagnostics and optional candidate summary |
| `POST /v1/projects/{id}/commands` | Typed command batch, revision precondition, idempotency key; structured change result |
| `PUT /v1/projects/{id}/source` | Parse and Replace with revision precondition; same validators and history |
| `POST /v1/projects/{id}/transitions/preview` | Direction, source selection, explicit mapping/field choices → concrete plan |
| `POST /v1/projects/{id}/transitions/apply` | Plan/fingerprint, choices, revision and idempotency key → atomic change |
| `GET /v1/projects/{id}/diagnostics` | Stable codes, severity, ObjectRef/field, related references |
| `GET /v1/projects/{id}/traces?object=...` | Both incoming and outgoing traceability, drift status, counterpart references |
Use structured serialization and publish an OpenAPI contract/example requests alongside the normative modeling specification. Invalid syntax is 400; invalid semantic candidates can use 422; stale revisions are 409/412 according to the documented conditional-request contract; missing projects/objects are 404. Never return a success status for a partial transition. Service revisions and external artifact revisions are distinct fields/types.
A single-file repository is sufficient initially. Hold a project write lock while checking revision, preparing, atomically persisting the candidate, and adopting it; all fallible validation/serialization occurs before adoption. A failed write leaves accepted memory/history unchanged. Crash after a successful disk replace reloads the persisted candidate. Retain an external-file fingerprint on load/save and reject stale overwrites instead of relying on `temp.persist` alone. Do not let a backend request provide an arbitrary host filesystem destination; it acts on configured project IDs/locations. Local-only binding can be the initial launch mode; remote deployment/authentication is a separate configured service capability.
## Language, stable diffs, highlighting, and linting
Schema 2 must persist aliases for elements, relations, traces, artifacts, diagrams and other source-addressable records. UUID remains identity; alias is an explicit human-readable property. Generate an alias once at creation and keep it across rename, reparent, layout, and layer transitions. Renaming a display name must change that string only. Renaming an alias is an intentional refactoring that updates printed references; UUID references internally remain stable.
Use a project-wide alias namespace initially. It avoids ambiguous references, path-dependent aliases that change when reparenting, and special quoting of cross-layer references. New generated collisions use a stable UUID-derived suffix; never rename existing objects to make room for an insertion. A user-supplied duplicate alias is an error. Keep UUID-based declaration ordering initially: it is stable on ordinary edits and does not reorder blocks when names/aliases change. Separate semantic records, links/traces, and diagram sections, with fixed field order and one independently editable property per line.
The conceptual source shape is:
```text
arcadia 2;
project mission "Coastal rescue" id "..." {
oa activity assess "Assess incident" id "..." {
description "Determine the response needed.";
parent coordinate;
}
sa function classify "Classify incident" id "..." {
description "Determine incident class.";
}
trace assess_classify "Function refinement" id "..." {
oa assess;
sa classify;
kind function_refinement;
rationale "Classification supports operational assessment.";
}
artifact assessment_requirement "REQ-42" id "..." {
owner classify;
relation satisfies;
uri "requirements://rescue/REQ-42";
revision "baseline-2026-09";
}
// Diagram node placements live in a separate diagram section.
}
```
This illustrates syntax direction only; `...` is not a valid UUID, and the excerpt omits the referenced parent declaration. Final grammar must define every record, field, default and rule before implementation. Keep JSON-compatible exact strings and finite-coordinate guarantees. Optional IDs remain useful for new authoring, but linting must not allocate new persistent IDs on each keystroke. Parse into syntax/semantic candidates with unresolved generated identities and materialize IDs once on successful Apply/creation. Saved text always includes IDs. Reparse of an existing ID-bearing document must preserve identity.
Expose `analyze(source) -> Analysis { tokens, syntax, references, diagnostics }` independently from strict `parse/validate` and commit. Tokens carry byte ranges and classes for keywords, layer/kind names, declarations/references, strings, numbers, punctuation, and comments. A tolerant lexer must still color incomplete strings/documents. Synchronize parser recovery at declaration/field boundaries to provide multiple useful errors. Core diagnostic codes, severity, primary range, ObjectRef/field path and related ranges serve both the Iced editor and REST linting. Preserve one-based Unicode-scalar display columns; future LSP adapters must convert explicitly to the negotiated position encoding.
Use debounced background analysis with a draft-generation number; discard stale results and never replace the committed model while typing. Warnings should include unallocated leaves, unsupported capabilities, process/scenario gaps, incomplete state initialization, and trace drift. Errors include illegal cross-layer ordinary relations, cycles, dangling references, duplicate identities/aliases, incompatible mappings, and bad field types. Quick fixes, if added, produce ordinary commands or explicit source edits.
V1 discards comments; V2 must state its preservation policy clearly. The smallest Git-friendly extension can retain normalized formatting while persisting model documentation in description/rationale fields. If comments must survive graphical edits, preserve attached trivia in a document layer keyed by IDs and reconcile it on Replace, with orphan-comment behavior and tests. Do not promise lossless source editing while continuing to overwrite `source_editor` from a comment-free printer. Syntax highlighting and useful linting do not require a full language server or a second semantic store.
## Deterministic schema-1 migration
Dispatch on format and version before decoding the final model. Keep strict `ProjectV1`/text-v1 readers and validate with the old rules; do not temporarily disable current validation or silently deserialize unknown fields. Reject future versions with a located/actionable message.
1. Preserve project, element, and relation UUIDs and every stored name/description exactly.
2. Map all five legacy kinds and four relation kinds to their explicit OA equivalents. Parent is absent; no SA system, actors, roles, or external requirements are invented.
3. Move each legacy element position into a generated OA overview node, preserving IEEE f32 values including signed zero in the representations where the existing contract requires it.
4. Preserve process step order and repeated interaction references. If occurrences gain identities, derive stable migration IDs from project UUID, process UUID, and original occurrence index.
5. Generate the OA overview and node IDs deterministically from a documented namespace; resolve even pathological collisions deterministically against the complete old/new identity set. Random migration IDs would make opening the same legacy file yield unrelated Git changes.
6. For legacy JSON, compute old canonical aliases once using the V1 algorithm and persist them. For V1 source, choose and specify one policy: retaining supplied aliases is more helpful to source authors; using V1 canonical aliases preserves the historical JSON/text equivalence baseline. Recommended compatibility policy is canonical V1 aliases for both, with an explicit import option to retain authored aliases if needed. Generate the newly introduced project/view/occurrence aliases after reserving all legacy object aliases so they cannot rename old references. No policy may silently change aliases on later saves.
7. Initialize new record collections empty, validate the complete V2 project, and return an import/migration report. Migration never overwrites the source merely because it was opened.
8. Save schema 2 explicitly. Do not label a V2 file schema 1 or silently drop SA/trace/artifact/behavior data on downgrade. A legacy export can exist only as a separately named, validated operation with a concrete loss report.
Migration should be idempotent and deterministic: migrating identical input produces identical V2 project and canonical text. Tests must compare the old/new semantic projection as well as full migrated text/JSON round trips; equality alone between two incorrectly migrated results would not prove preservation.
## Graphical/textual parity and acceptance gates
Maintain a field matrix in the V2 specification: each persistent mutable field names its source syntax, graphical editor/control, core command, validator rule, and round-trip fixture. This includes aliases, parents, trace endpoints/mapping/rationale/baselines, arbitrary artifact URI/revision/fragment, scenario branches/guards/occurrences, states/transitions, item fields, and diagram coordinates. A read-only inspector does not meet editing parity. System-generated identifiers/baselines may have dedicated explicit operations, but both representations must expose their supported editing contract.
Preserve the established draft guard: source and property drafts cannot coexist as competing writers. Layer/view switches, transition launch, artifact edits, hierarchy actions, and mode/scenario editors first apply or explicitly revert the active draft. Invalid source keeps its buffer and Help remains read-only/reachable. A source draft carries the base accepted revision, so remote changes produce an explicit conflict instead of being overwritten by Apply. Command results carry all identity remaps for selections, pickers, diagrams and transitions; do not extend the current handful of `Message::Command` selected-ID rewrites for each new feature.
Meaningful new verification should cover:
* Full-field V2 parse/print and JSON equality with OA+SA, hierarchy, repeated occurrences, branches, modes/states, items, artifacts and traces; canonical idempotence and exact strings/coordinates.
* Display rename changes only its field; inserting a colliding-name object preserves existing aliases/references; reparent changes only its parent line; arrangement changes only the active diagram section.
* Every legal/illegal endpoint and containment family, direct allocation cardinality, parent cycles, state/occurrence references, no accidental cross-layer ordinary relations, and same-kind cross-layer confusion rejection.
* Forward and reverse traced creation, reuse, update and manual merge; unchanged repeat is a no-op; one-to-many ambiguity, unmapped endpoints, stale plans, repeated API requests and failed writes cannot leave partial results or duplicate counterparts.
* Drift from either side, both sides, layout-only changes, alias rename, deletion and identity changes; deleting a traced source preserves the counterpart and provides an explicit trace impact result.
* Cascade/identity reference coverage over every reference-bearing record, with one Undo restoring the entire transaction and failed operations preserving redo.
* Deterministic migration from both bundled schema-1 formats, malformed/future input rejection, old-field preservation and collision handling for generated IDs.
* GUI editing for every mutable field and text→graph→text round trips, nested drag/reparent distinctions, multiple-view Fit/SVG, invalid drafts across layer switches, and actual OA→SA→OA preview/commit workflows.
* REST contract tests proving parity with in-process commands, stale ETag/revision rejection, structured diagnostics, arbitrary URI storage without fetch, and source Replace using the same validation.
Recommended implementation order is schema/spec and migration; transaction/reference hardening; OA hierarchy/roles and SA semantic records; source/parser/printer parity; traced transition planner; graphical views/editors; shared analysis/highlighting/linting; REST adapter; then cross-platform builds and actual GUI/API acceptance. Ship no V2 feature as complete before both editing forms and its rejection/undo/migration paths are verified.
+175
View File
@@ -0,0 +1,175 @@
# V2 OA / SA semantics research
Research date: 2026-09-05. This is an implementation decision aid, not a claim that the proposed features already exist. It distinguishes **Arcadia method**, **Capella implementation evidence**, and **recommended Studio behavior**.
## Evidence and limits
Sources are the official Arcadia reference site and the Eclipse Capella repository. Repository source inspection was pinned to commit `3153b930a4284f6c82952658f1e8d8d6d8c8f340` where indicated; user documentation links below use `master` and can change. Relevant original screenshots were also extracted from the already-installed Capella 7.0.1 documentation JARs and visually inspected. This researcher did not operate a live Capella session. Native runtime inspection is separate work, and old documentation screenshots must not be presented as screenshots of that runtime.
The Ecore schemas expose inherited or permissive structures that are further constrained by validation rules. For example, `SystemComponent` structurally has a child-component reference, while the SA validation rules forbid internal decomposition of the System. Schema availability alone is not evidence that a concept is legal in an engineering perspective.
No source code or visual assets were copied into Studio. The recommendations below are an independent synthesis. They do not establish Capella file compatibility, full metamodel conformance, simulation semantics, or support for Logical/Physical Architecture.
## Perspective boundaries
**OA describes stakeholder work and its operational context.** Identify what people, organizations, and other entities must accomplish, their capabilities, activities, interactions, and operational conditions. Avoid assuming the future System of Interest as the solution. **SA describes the selected system contribution:** its boundary, externally visible services, interactions with operators and external systems, and expected behavior. A single operational activity may require several system and actor functions; different solution alternatives can satisfy the same operational need. [Arcadia Q&A][arcadia-qna]
An OA Actor is an undecomposable operational participant, usually human. An SA Actor is external to the System; it can be human or another system. Consequently, OA Entity → SA Actor is normal and does not mean that the stakeholder became a person. Missions express high-level goals; capabilities express abilities that support them. Activities/functions describe work, and interactions/exchanges describe dependencies between that work. [Arcadia language][arcadia-language]
SA has exactly one System, alongside its external actors. It does not contain the System's internal logical components, software/hardware placement, or deployment nodes. Those belong to subsequent architectural perspectives. SA functions can still have deep decomposition: a functional breakdown does not decompose the System structurally. [Capella architecture diagrams][features], [component rules DWF_DC_36–38][validation-components]
## Three meanings of nested boxes
| Meaning | Semantic operation | OA / SA application | V2 editing contract |
|---|---|---|---|
| Decomposition / containment | Change an element's semantic parent | OA entity within entity; activity within activity; SA function within function; non-human external actor hierarchy where supported | Explicit **Move into / Reparent** command. One parent, same perspective, compatible types, no cycles. Move subtree as one undoable transaction. |
| Allocation presentation | Show work inside its performer | OA activities inside entities/actors; SA functions inside the System/actors | Explicit **Allocate to** command. Moving a function visually is insufficient to change its functional parent. Inspector shows both functional parent and performer. |
| Deployment / hosting | Allocate a behavioral implementation onto hosting resources | Physical Architecture, outside this OA/SA scope | Do not expose a generic SA deployment relation or reinterpret every nested box as a hardware node. |
Capella allows direct allocation of **leaf** functions/activities. A non-leaf function may appear within a performer as a computed summary of its allocated leaves. That appearance is not a second direct allocation. The official allocation guide also distinguishes functions allocated on subcomponents using a dashed presentation and parent functions using italics. V2 can use its own accessible styling, but must preserve the distinction. [Allocation guide][allocation-guide]
Implementation consequence: retain independent references for `semantic_parent`, allocation, and diagram containment/presentation. Dragging an activity into an entity should propose allocation; dragging it into another activity should propose decomposition. When splitting an allocated leaf, offer an atomic operation that moves its allocation to the new leaves; never silently leave an illegal direct allocation on their parent. This is a Studio design recommendation.
The direct-allocation uniqueness rule counts `ComponentFunctionalAllocation` relationships. It does **not** count role-mediated `ActivityAllocation` relationships. Do not incorrectly outlaw OA role sharing by treating every role membership as a direct performer allocation. [Unicity implementation][allocation-unicity]
## Prioritized V2 semantics matrix
P0 means foundational work needed before the dependent rows. P1 means the next implementation group for a meaningful OA/SA workbench, not permission to claim complete V2 while omitting it. P2 covers richer variants that require explicit scope disclosure if absent.
| Priority / concept | OA semantics | SA semantics | Concrete V2 minimum and validation |
|---|---|---|---|
| P0 — perspective and identity | Operational analysis root; typed entities, actors, activities, capabilities | System analysis root; exactly one System; external actors, functions, missions, capabilities | Every element/relation has stable identity and perspective. Reject cross-layer containment and same-layer work allocation to another layer. |
| P0 — structural hierarchy | Entities can own subentities; OA actors cannot decompose | No internal System component tree; non-human external actor composition is distinct from the System | Parent picker plus nested diagram and tree. Prevent cycles. Human participants cannot acquire children. [OA schema][schema-oa], [component rules][validation-components] |
| P0 — work breakdown | Activities have owned activities | System functions have owned functions; functions may be allocated to the System or an Actor | Leaf/non-leaf distinction; create child, reparent, collapse, expand, focus subtree. Preserve independent performer. [functional schema][schema-fa], [SA schema][schema-sa] |
| P0 — direct work allocation | Entity/Actor → leaf Activity | System/Actor → leaf System Function | At most one direct performer; reject wrong perspective and non-leaf direct allocation. Unallocated leaves are an actionable completeness issue while authoring. [allocation implementation][allocation-types] |
| P0 — exchanges | Directed Interaction between Activities; communication means connect Entities/Actors | Functional Exchange between output/input function ports; Component Exchange between System/Actor boundaries | Typed endpoints, direction, exchange-item references. An exchange is not an allocation or a sequence occurrence. Distinct parallel exchanges retain distinct IDs. [functional schema][schema-fa], [OA schema][schema-oa] |
| P0 — operational/system goals | Operational capabilities involve Entities/Actors and work/processes | Mission exploits Capability; Capability involves System/Actors, functions, chains/scenarios | Typed involvement and mission-capability links; browse from both endpoints. Do not label all of these relationships “realization.” [SA schema][schema-sa], [interaction schema][schema-interaction] |
| P0 — traced counterpart workflow | Can select work/participants/goals to realize in SA | Realizing elements retain links to operational sources | Explicit creation, linking, comparison, and update in either direction; stable many-to-many traces; atomic apply/undo. Detailed contract below. |
| P1 — Roles | A coherent activity group can be assigned to one or more Entities/Actors | No direct SA Role equivalent | Separate Entity→Role and Role→Activity relations; show effective assignments. Transition shared roles using contextual function instances or explicit assignment decisions, not multiple direct allocations of one SA function. [Role glossary][role], [OA schema][schema-oa] |
| P1 — Processes / Functional Chains | Operational Process references activities/interactions along a path | Functional Chain references functions/exchanges along a path | Ordered occurrence records with stable IDs, including repeated use of the same semantic exchange; continuity checks; capability involvement; highlight the path in diagrams. A flat list alone does not implement branching control semantics. [Arcadia language][arcadia-language], [chain rules][validation-dataflows] |
| P1 — Scenarios | Activity and Entity scenario views use operational interactions | Functional, exchange, and interface scenario views use appropriately typed lifelines/messages | Scenario belongs to a capability; explicit lifelines, ordered messages invoking existing exchanges/operations, pre/postconditions, and referenced states. Retain message identity separately from exchange identity. [scenario schema][schema-interaction], [scenario rules][validation-scenarios] |
| P1 — Modes / States | Owned by an operational participant | Owned by the System or an Actor | State-machine owner; regions; state/mode/initial/final kinds; directed transitions, guard, trigger, effect; entry/do/exit work references; availability of activities/functions, processes/chains, and capabilities. No machine execution claim. [state schema][schema-state] |
| P1 — Data and Exchange Items | Operational information conveyed by interactions | Typed information at the system boundary and functional exchanges | Exchange Item with ordered named typed elements, direction/multiplicity where supported; reusable primitive/enum/record types; exchange↔item links. Avoid hard-coding software-only request/response data. [information schema][schema-information] |
| P1 — Interfaces | Use operational communication means and exchanged information; do not import implementation contracts indiscriminately | System/Actor interfaces group Exchange Items; participants or their ports provide/require interfaces | Interface contains references to exchange items; explicit provided/required direction and port ownership. Functional exchange is not itself an interface. [Interface glossary][interface], [structural schema][schema-structure] |
| P1 — Communication support | Communication Mean can carry operational Interactions | Component Exchange can carry Functional Exchanges | Explicit exchange-allocation relation, compatible performer endpoints, same perspective; boundary exchange and work exchange remain separate records. [OA schema][schema-oa], [component rules DWF_DC_13/27/29][validation-components] |
| P1 — conditions and constraints | Stakeholder context, operational assumptions, performance and constraints | Required qualities, boundary constraints and acceptance conditions | Named text constraints with typed targets and units as data where numeric. Treat prose guards/constraints as descriptions unless an evaluator is actually implemented. [Arcadia perspective overview][arcadia] |
| P2 — richer behavior | Role context, ALT/PAR/LOOP scenarios, process control nodes | ALT/PAR/LOOP scenarios, chain control nodes, composite/concurrent state regions | Explicit control/operand structures and ordering; same-layer scenario references; reject malformed fragments. No flattened “sequence” advertised as equivalent. [diagram tools][diagrams], [scenario rules][validation-scenarios] |
| P2 — capability relations | Include, extend, generalize Operational Capabilities | Include, extend, generalize System Capabilities | Distinguish these relations from decomposition and realization; disallow self-relations and diagnose cycles according to the declared Studio profile. [capability rules][validation-capabilities] |
Arcadia describes operational missions, but the inspected Capella OA schema has no matching `Mission` class; the class exists in SA. If V2 includes an OA Mission, describe it as support for the Arcadia method, with a Studio-defined representation, rather than claiming exact Capella OA metamodel equivalence. [Arcadia Q&A][arcadia-qna], [OA schema][schema-oa], [SA schema][schema-sa]
The MVP's activity→capability “Contribution” and process→capability “Realization” are Studio concepts. For V2, use capability **involvement** for local work/process links, and reserve cross-perspective **realization** for traceability, or explicitly document any retained legacy terminology and migration. Capella has dedicated capability involvement records. [interaction schema][schema-interaction]
## Transition mappings and their limits
Capella's transition documentation describes separate entry points and dependency handling. It is not a universal clone-all command. Operational Entity can transition to Actor or be realized by the System. Operational Capability can transition to Capability or Mission. Transitioning an entity can also transition its allocated activities/interactions. The exact dependency closure must be shown before applying a Studio transition. [Capella transition entry points][transition-entry]
| OA source | SA counterpart | Required Studio decision |
|---|---|---|
| Entity / Actor | External Actor, or the existing System | Decide scope deliberately. Several OA participants may contribute to defining one system boundary. Do not create a second System. |
| Activity | One or more System Functions, allocated to System and/or Actors | Select granularity and responsibility. Preserve independent decompositions. |
| Interaction | One or more Functional Exchanges | Resolve realizing source/target functions; create required ports; retain information-item links. No exchange with unmapped endpoints. |
| Communication Mean | Component Exchange | Require mapped participant endpoints and reconcile exchange allocations. |
| Operational Capability | System Capability or Mission | Choose ability versus high-level goal; do not infer solely from the label. |
| Process | Functional Chain | Map occurrence identities and their work/exchanges; preserve repeated occurrences. Branching and split mappings require explicit resolution. |
| Operational Scenario | Suitable SA scenario | Resolve lifeline/operation types, ordering and referenced states; carry a scenario realization link. |
| Modes/States and transitions | Separate SA-owned machine elements | Resolve owner first, then state/transition mappings, then availability/trigger links. Do not point an SA machine at OA-owned states. |
| Exchange Item / datatype | Shared upper-layer information, or transitioned counterpart | Make reuse versus creation visible. Capella permits upper-layer data/item reuse in downstream layers; it does not permit the same cross-layer reuse for states/modes. |
| Role | No automatic same-kind SA counterpart | Use its activity/performer context to produce suitable functions and actors. |
The first five rows follow the documented entry points and inspected OA/SA/functional schemas; scenario/state/item rows are supported by their realization-bearing schemas and transition dependency documentation. This table is a Studio mapping contract proposal, not evidence that one Capella menu implements every row in both directions. [transition entry points][transition-entry], [functional schema][schema-fa], [scenario schema][schema-interaction], [state schema][schema-state]
Capella's documented transition engine is conservative and iterative: traced targets can be updated; deleting a source does not automatically destroy the target; rerunning can recreate a deleted target. Its merge UI compares the candidate transition result with the existing target. This supports reviewed updates, but it is not evidence of automatic, lossless reverse transformation. [Transition concepts][transition-concepts]
### Explicit bidirectional creation and update for Studio
The user request calls for more than browsing. Implement the following in both OA→SA and SA→OA. These are recommended Studio behaviors; the reverse authoring workflow extends the documented Capella transition workflow.
1. **Create counterpart / Link existing.** Start from either a diagram selection, the model tree, inspector, or source selection. Choose counterpart kind, destination parent, and involved performers. Reuse an existing trace when present; show multiple counterparts rather than silently choosing one.
2. **Keep canonical semantics.** The relation always means “SA element realizes OA element,” even when the command starts in SA. Command direction belongs to transition history, not the meaning of the relation.
3. **Make reverse creation explicit.** SA Function can propose an OA Activity; SA Actor can propose an OA Actor/Entity according to its human/context meaning; Capability can propose Operational Capability. An SA System must not automatically create an OA entity representing the solution under study. An SA interface or internal system-only function may have no useful direct OA counterpart. Let the engineer author or select the operational need and record rationale.
4. **Preview a dependency-complete change set.** Show new counterparts, existing matches, hierarchy changes, proposed allocations, exchanges/ports, process/scenario occurrences, reused information, and links that cannot map. Apply only when required endpoint/ownership choices are resolved. No implicit fallback to unrelated same-name elements.
5. **Separate propagation from editing.** Rename/edit either side normally. Mark affected traces “review needed.” An explicit **Compare / Update counterpart** action offers per-field Keep target, Apply source proposal, or Edit value. Descriptions and names commonly diverge legitimately between needs and system services.
6. **Detect concurrent semantic changes.** Store a last-reviewed semantic snapshot/signature per trace or transition group. Distinguish source-only, target-only, and both-changed cases. Exclude diagram layout from semantic drift. A previous review baseline is needed; comparing names alone is insufficient.
7. **Preserve identity and local work.** Repeated transition/update is idempotent when nothing changed. Selected changes commit as one undoable transaction. Deletion of one side removes/marks the trace appropriately and offers impact review; it never cascades into the other perspective without an explicit separate model deletion.
8. **Navigate both ways.** Each inspector exposes “Realizes” and “Realized by,” shows counterpart kinds and layers, and opens the counterpart diagram/source location. Back/forward navigation preserves context. Display a trace table for coverage and unresolved decisions.
Many-to-many realization must remain possible. A deterministic update algorithm can safely propose changes for a simple one-to-one pair, but cannot decide how to merge multiple operational activities into one function, or split an exchange across several functions, without engineering choices. Recording an intentional divergence is a successful review outcome, not a synchronization failure.
## Behavior rules worth implementing early
The following constraints prevent plausible-looking diagrams from contradicting their model:
- **Scenarios:** message endpoints must match the referenced interaction/exchange and lifeline types; starts precede corresponding finishes; message exchange items must be supported by the invoked exchange/operation; referenced scenarios stay in the same architecture layer. Capella distinguishes OA `INTERACTION` scenarios from SA `FUNCTIONAL`, `DATA_FLOW`, and `INTERFACE` kinds. User-facing names can be clearer. [scenario validation][validation-scenarios]
- **State machines:** validate initial-state cardinality and initial transition count; owner-relative entry/do/exit work and triggers; references to available functions; consistent regions. Capella makes mixed Mode/State hierarchies preference-dependent, so Studio must publish its chosen policy rather than call a stricter rule universally required by Arcadia. [state validation][validation-states]
- **Chains/processes:** sequence/control links must refer to the actual involvement records and have consistent direction. Displaying a parent function's computed exchange is not a new semantic exchange. [chain validation][validation-dataflows], [computed links][features]
- **Capabilities:** self-inclusion, self-extension, and self-generalization are errors. Mismatches between involved actors and actual scenario/process participants are coverage diagnostics. [capability validation][validation-capabilities]
- **Structural versus completeness issues:** reject dangling references, wrong kinds/layers, cycles, and invalid uniqueness; keep missing allocation, uncovered capability, incomplete scenario, or absent realization as visible modeling diagnostics when safe. Do not prevent engineers saving legitimate work in progress.
## Original UI evidence and adaptation
Four original screenshots were visually inspected from installed `org.polarsys.capella.ui.doc_7.0.1.202503211540.jar` and `org.polarsys.capella.transitions.doc_7.0.1.202503211540.jar`. Matching official repository URLs are listed here so the evidence remains reviewable:
| Original evidence | Observed directly | Useful V2 adaptation |
|---|---|---|
| [Capella perspective screenshot][shot-perspective] | Left model hierarchy; central Activity Explorer with OA/SA tabs; properties, information, semantic browser areas | Keep OA/SA discoverable and provide task guidance; selection coordinates all editing views. |
| [Semantic browser screenshot][shot-semantic] | Three columns for referencing/current/referenced elements; explicit realizing and realized functions, parent, exchanges and related diagrams | A focused relationship inspector with both directions, parent, allocation, and direct diagram/source navigation. |
| [First transition example][shot-transition] | Separate OA/SA model trees; activity breakdown becomes a function breakdown; the target diagram needs representation setup | Never equate model transition with copying a picture. Offer to create/focus the target view after creating semantic elements. |
| [Allocated functions screenshot][shot-allocation] | Functions drawn inside component boxes while exchanging with functions allocated elsewhere | Nested allocation view; independent work hierarchy. This screenshot uses LA and is evidence of presentation only, not permission for SA internal components. |
Documentation also confirms the semantic browser has navigation history and can follow selection, and that diagram insert/remove operations can alter graphical presence without deleting semantic elements. V2 should therefore separate **Hide from diagram** from **Delete from model**. [Semantic browser guide][semantic-browser], [diagram tools][diagrams]
These images are documentation evidence, not a current-version usability benchmark. No video was played, no timing/accessibility claim was measured, and no claim is made that every depicted command is available unchanged in the installed runtime.
## Graphical / textual parity and reviewable completion
Recommended acceptance criterion: every implemented semantic field, reference, hierarchy, scenario occurrence, state-machine relation, interface declaration, and realization decision can be created and edited graphically and textually against one UUID-based model. Graphical diagrams are views, not a second source of truth. If layout/view filtering is persistent, its syntax and persistence belong to the same parity contract.
Useful acceptance examples should cover a **general system**, such as a rail crossing, rescue service, or industrial water system, with humans, organizations, external systems, physical/information interactions, operating modes, and degraded behavior. Avoid making every function an API call or every item a software message.
The most discriminating end-to-end cases are:
1. Decompose OA entities and activities, allocate leaves, show a computed parent summary, and reparent a work subtree without changing its performer.
2. Realize one OA activity through both an SA System function and an Actor function; traverse the traces both ways.
3. Create an OA counterpart from an existing SA function, retain the SA→OA realization direction, edit both sides independently, and resolve an explicit update without losing either draft.
4. Transition a role shared by two participants without creating a multiply allocated SA function.
5. Model a process/chain and a scenario with two occurrences of one exchange; save/reopen and undo while preserving occurrence identity and order.
6. Link a degraded mode to available work and a guarded transition; show that incorrect owner references are rejected.
7. Convey a typed exchange item through a functional exchange and required/provided interface; show valid endpoint and item navigation in diagram, inspector, and source.
8. Apply text edits that change containment, allocation, and realization in one transaction; malformed input preserves the prior model. Round-trip both storage forms without dropping newly introduced fields.
Completeness must be reported feature by feature. A new enum value, label, inspector field, or sample diagram is insufficient evidence that a semantics row works through editing, validation, persistence, undo, navigation, and both representations.
## Primary references
The inline references above link directly to the supporting material. The core source groups are the [Arcadia method overview][arcadia] and [language reference][arcadia-language], the [Capella OA][schema-oa] / [SA][schema-sa] schemas, and the official [transition][transition-entry], [allocation][allocation-guide], and [validation][validation-components] documentation. Schema links are pinned; documentation links describe the inspected repository state and may evolve.
[arcadia]: https://mbse-capella.org/arcadia.html
[arcadia-qna]: https://mbse-capella.org/arcadia-qna.html
[arcadia-language]: https://mbse-capella.org/resources/arcadia-reference/Arcadia%20Language%20-%20MetaModel.pdf
[features]: https://mbse-capella.org/features.html
[schema-oa]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/OperationalAnalysis.ecore
[schema-sa]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/ContextArchitecture.ecore
[schema-fa]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/FunctionalAnalysis.ecore
[schema-interaction]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/Interaction.ecore
[schema-state]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/CapellaCommon.ecore
[schema-information]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/Information.ecore
[schema-structure]: https://github.com/eclipse-capella/capella/blob/3153b930a4284f6c82952658f1e8d8d6d8c8f340/m2/plugins/org.polarsys.capella.core.data.def/model/CompositeStructure.ecore
[allocation-guide]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.ui.doc/html/05.%20Diagram%20Management/Allocating%20Functions%20on%20Components.mediawiki
[allocation-unicity]: https://github.com/eclipse-capella/capella/blob/master/core/plugins/org.polarsys.capella.core.data.fa.validation/src/org/polarsys/capella/core/data/fa/validation/function/MDCHK_Function_ComponentAllocationUnicity.java
[allocation-types]: https://github.com/eclipse-capella/capella/blob/master/core/plugins/org.polarsys.capella.core.data.cs.validation/src/org/polarsys/capella/core/data/cs/validation/component/MDCHK_Component_AllocatedFunctions_1.java
[role]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.glossary.doc/html/Glossary/78.%20Role.mediawiki
[interface]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.glossary.doc/html/Glossary/45.%20Interface.mediawiki
[diagrams]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.diagrams.doc/html/Diagrams/diagrams.mediawiki
[validation-components]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.validation.doc/html/Validation%20Rules/design/well-formedness/components/ValidationRules.mediawiki
[validation-dataflows]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.validation.doc/html/Validation%20Rules/design/well-formedness/dataflows/ValidationRules.mediawiki
[validation-scenarios]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.validation.doc/html/Validation%20Rules/design/well-formedness/scenarios/ValidationRules.mediawiki
[validation-states]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.validation.doc/html/Validation%20Rules/design/well-formedness/state_machines/ValidationRules.mediawiki
[validation-capabilities]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.validation.doc/html/Validation%20Rules/design/well-formedness/capabilities/ValidationRules.mediawiki
[transition-entry]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.transitions.doc/html/11.%20Iterative%20Transitions/11.2.%20Transitions%20Between%20Engineering%20Phases.mediawiki
[transition-concepts]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.transitions.doc/html/11.%20Iterative%20Transitions/11.1.%20Concepts.mediawiki
[semantic-browser]: https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.ui.doc/html/04.%20User%20Interface/4.07.%20Semantic%20Browser.mediawiki
[shot-perspective]: https://raw.githubusercontent.com/eclipse-capella/capella/master/doc/plugins/org.polarsys.capella.ui.doc/html/04.%20User%20Interface/Images/4.01_Capella_Perspective.png
[shot-semantic]: https://raw.githubusercontent.com/eclipse-capella/capella/master/doc/plugins/org.polarsys.capella.ui.doc/html/04.%20User%20Interface/Images/4.07.%20Semantic%20Browser_html_m2d24d060.png
[shot-transition]: https://raw.githubusercontent.com/eclipse-capella/capella/master/doc/plugins/org.polarsys.capella.transitions.doc/html/11.%20Iterative%20Transitions/Images/11.3_Exemple1.png
[shot-allocation]: https://raw.githubusercontent.com/eclipse-capella/capella/master/doc/plugins/org.polarsys.capella.ui.doc/html/05.%20Diagram%20Management/Images/AllocatingFunctions_FunctionAllocatedOnComponents.png
+290
View File
@@ -0,0 +1,290 @@
# Arcadia Studio V2: deployable model service
Research/design proposal, 2026-09-05. This document describes work to implement; it is not evidence that a server, V2 schema, or deployment currently exists. The V2 normative model contract must define the exact layer/kind/alias/transition fields before implementation. API examples below propose wire DTOs over that contract.
## Recommendation and current implementation boundary
Add one `oa-server` workspace crate producing `arcadia-server`, using Axum/Tokio for HTTP and the same `oa-core` model, typed commands, canonical language, transition planning, and validation as the desktop. Keep HTTP/authentication/storage code outside `oa-core`. Use one local SQLite database through `rusqlite` as the initial deployed store; do not add an ORM, plugin runtime, graph database, message broker, or distributed coordination system.
The inspected code already supplies most of the domain foundation:
| Existing code | Reuse / change needed |
| --- | --- |
| `crates/oa-core/src/model.rs` | UUID-addressed ordered element/relation maps, schema 1, closed OA kinds, ordered repeated process steps. V2 owns its new concepts and persistent aliases. |
| `session.rs`, `Session::execute` | Clones a candidate, applies one command, validates, then adopts it. Extract a reusable candidate reducer and final validation boundary for atomic batches. Preserve existing desktop undo semantics. |
| `session.rs`, `reidentify` and `Delete` | Maintains graph references. Extend in the core for V2 live references, aliases, and trace records; historical revision anchors remain immutable. |
| `language.rs` | Strict parsing and deterministic source printing. Schema 1 regenerates aliases and discards comments; V2 persistent aliases must survive through the shared V2 parser/printer and JSON, not a service-only alias map. |
| `persistence.rs` | Strict candidate loading and sibling-temp-file replacement. Useful for local files/export, but currently stores only `Project` and has no revisions or retry ledger. |
| `validation.rs` | Shared warnings/errors. Add stable issue codes and locations; currently `Issue` has a message and optional object ID, and `ModelError` is string-only. |
Existing `Add`/`Connect` generate identities and use UI-oriented defaults. API create commands need explicit client UUIDs and complete records. Existing step commands silently ignore invalid indices, and deleting an unknown object is a no-op; the API contract should reject unintended missing objects/indices explicitly. Add those rules to shared typed commands rather than implementing a second mutation engine in HTTP handlers.
### Why SQLite; viable file alternative
A single `StoredProject { resource_id, revision, model: ProjectV2 }` envelope can atomically store model, artifacts, and trace links together using the existing temp-file mechanism. There is no inherent need for separate database tables for these model records. This is a good minimal option if the required guarantee is just replacement of the current snapshot.
The service proposed here additionally promises retained revisions, atomic optimistic concurrency checks, durable idempotency receipts, and revision-bound transition previews. SQLite allows those records and the current-head pointer to commit in one transaction. With files, use immutable complete revision envelopes plus an atomically replaced head manifest containing the receipt information; never independently replace a model file and a metadata file and call the pair atomic. The file approach needs explicit orphan recovery, directory-sync/platform semantics, receipt retention, and cross-process locking. SQLite is the recommended tradeoff for the deployable V2 service. Its atomic-commit documentation describes both the guarantees and filesystem assumptions. [SQLite atomic commit](https://www.sqlite.org/atomiccommit.html).
## Resource and version contract
- Base URL: `/api/v1`. HTTP API version, database migration version, and model schema version are independent.
- Each hosted project has an immutable service `resource_id` UUID. `ProjectV2.id` remains a model identity and may be edited using the shared identity command. URLs, receipts, and historical trace anchors use `resource_id`; changing a model ID never changes its URL. Imported copies can share model IDs, so a model UUID alone is not a global service address.
- Generate and persist one `service_id` UUID when initializing the store. Qualify historical anchors with it so exported evidence cannot accidentally resolve against an unrelated server. Preserve it during disaster-recovery restore; creating an independent service copy requires an explicit new identity and retains imported anchors as external evidence.
- A persisted revision is a positive, monotonically increasing signed-64-bit integer, represented as a decimal **string** in JSON to avoid JavaScript precision loss. A create starts at `"1"`. Every changed model/metadata commit increments once; a no-op returns `changed: false` without a new revision.
- All project views expose the snapshot revision and a quoted project validator, for example `ETag: "p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1"`. API conditional writes compare that validator with the project's current head inside the storage transaction. Each URI has deterministic representation bytes at a given revision; negotiate any future encoded variants correctly.
- Require one exact `If-Match` project validator on every mutation of an existing project. Missing is `428`; stale is `412`; weak tags, lists, and `*` are rejected by this API. GET supports `If-None-Match`/`304`. The validator is opaque to clients. [HTTP conditional request semantics](https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match).
- Require `Idempotency-Key: <UUID>` for committing requests, including project creation/import/deletion and transition commit. Authenticate first, then look up a successful receipt **before** checking `If-Match`. The same principal/key and same method, path, body, and precondition replay the recorded result, even when head has advanced; a different request under the same key is `409 idempotency_key_reused`.
- Retain successful receipts, including no-ops, for 30 days; publish this window in OpenAPI. Mutation provenance retains request IDs with revisions indefinitely. After expiry clients must read state before deciding whether to send a new intent.
- JSON rejects unknown fields, invalid enum values, duplicate object-map keys, invalid UUIDs, and unsupported schemas. All model import paths run the same structural validator. Warnings allow incomplete engineering work and are returned separately.
## Minimum routes
`{p}` below always means service `resource_id`; `{r}` means an immutable revision. Route-specific limits and all DTOs belong in an OpenAPI 3.1 document shipped with the binary and available at `/api/v1/openapi.json`.
| Method/path | Contract |
| --- | --- |
| `GET /health/live` | `200` while the HTTP process can answer; no model details or credentials. |
| `GET /health/ready` | `200` after database/migrations/recovery succeed; `503` when shutting down or storage is unavailable. |
| `GET /api/v1/openapi.json` | Exact API schemas, supported enum vocabulary, limits, auth and error responses. |
| `POST /api/v1/projects` | Create an empty V2 model from `{"name":"Harbor assistance"}`; return `201`, `Location`, project metadata, and ETag. |
| `GET /api/v1/projects?limit=100&cursor=...&q=harbor` | Paged project summaries, not complete graphs. Exclude tombstones. |
| `GET /api/v1/projects/{p}` | Metadata: service/model IDs, name, head revision, model schema, counts, created/updated timestamps. |
| `PATCH /api/v1/projects/{p}` | Convenience rename with strict `{"name":"..."}` DTO; uses the same transaction service as commands. |
| `DELETE /api/v1/projects/{p}` | Conditional tombstone, one revision, `204`. Hide from normal reads; preserve audit/history. Physical purge is a separate future administrative operation. |
| `POST /api/v1/projects/import` | Create from raw canonical source or native model JSON, selected by `Content-Type`. Retain model identities and V2 aliases; assign a new service resource ID. |
| `GET /api/v1/projects/{p}/model[?revision={r}]` | Complete native `ProjectV2` JSON, suitable for lossless model export/import. |
| `PUT /api/v1/projects/{p}/model` | Conditional full-model JSON replacement, shared schema/validation, one revision. |
| `GET /api/v1/projects/{p}/source[?revision={r}]` | Canonical UTF-8 Arcadia source; lossless for every persistent V2 model field. |
| `PUT /api/v1/projects/{p}/source` | Conditional raw UTF-8 source replacement; preserve draft on the client if rejected. |
| `GET /api/v1/projects/{p}/elements` | Paged filter by `layer`, `kind`, `alias`, `q`, and optional `revision`; return complete selected records. |
| `GET /api/v1/projects/{p}/elements/{id}` | One record, optional `revision`. |
| `GET /api/v1/projects/{p}/relations` | Paged filter by `kind`, `source`, `target`, `incident_to`, optional `revision`. |
| `POST /api/v1/projects/{p}/query` | Read-only bounded graph neighborhood query; explicit roots, direction, kinds and depth. No SQL or executable expressions. |
| `GET /api/v1/projects/{p}/validation[?revision={r}]` | Structural issues and engineering warnings for a stored snapshot. |
| `POST /api/v1/projects/{p}/validation` | Validate a submitted JSON/source candidate, without changing head/history. |
| `POST /api/v1/projects/{p}/transactions` | Ordered typed batch; optional `dry_run: true`; all-or-nothing final validation. |
| `GET /api/v1/projects/{p}/revisions?after={r}&limit=100` | Ascending immutable revision summaries, provenance and command summaries; usable as a polling change feed. |
| `GET /api/v1/projects/{p}/revisions/{r}` | Snapshot envelope, provenance and change summary at one revision. Available for tombstoned projects to authorized clients. |
| `POST /api/v1/projects/{p}/transition-plans` | Run a core-defined layer transition in preview mode against an exact `If-Match` revision; return stored plan, generated aliases/IDs, diagnostics, effects, and expiry. Does not change model revision. |
| `POST /api/v1/projects/{p}/transition-plans/{plan}/commit` | Commit the stored typed plan only against its original base revision; revalidate and create one revision. |
| `GET /api/v1/projects/{p}/artifacts` | Paged model artifact records, filter by kind and optional revision. |
| `GET /api/v1/projects/{p}/trace-links` | Paged trace records, filter by artifact/object/predicate/revision. |
| `GET /api/v1/requests/{key}` | Authenticated receipt lookup for the same principal: resulting project/revision/status. `404` means no committed receipt is currently known, not proof that an accepted request cannot still complete. |
Use `application/json` for model JSON and `text/plain; charset=utf-8` for Arcadia source. Both import and replacement routes reject unsupported media with `415`; HTTP does not guess formats from filenames. Candidate validation accepts `{"format":"arcadia","source":"..."}` or `{"format":"json","model":...}`. It returns `200 {valid, issues}` for a well-formed validation request, including an invalid candidate; a rejected committing request returns `422`. Source diagnostics include one-based line/column using the core's documented Unicode-scalar convention.
Model JSON/source exports carry the V2 model, including whatever artifact/trace records the V2 normative schema defines. Service revision history, auth configuration, retry receipts, and pending plans belong to service backup, not model interchange. Until V2 parser/printer coverage exists, do not claim source/JSON parity for added metadata. Replacing from an older schema requires an explicit supported migration; it must not silently discard V2 data.
### Create and query examples
```http
POST /api/v1/projects HTTP/1.1
Authorization: Bearer <token>
Idempotency-Key: d06fbc2d-f953-4a59-a981-c6a9fc37cc8e
Content-Type: application/json
{"name":"Harbor assistance"}
```
```http
HTTP/1.1 201 Created
Location: /api/v1/projects/6f7162cc-094c-4f30-9b70-3f6f355f0c7f
ETag: "p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1"
Content-Type: application/json
{
"resource_id":"6f7162cc-094c-4f30-9b70-3f6f355f0c7f",
"model_id":"debe7a6a-5a87-4d77-ad9d-39b7b5c4c608",
"name":"Harbor assistance",
"revision":"1"
}
```
```json
{
"revision":"3",
"roots":["1b37b9d7-9a95-4a51-8707-010878a82419"],
"direction":"both",
"relation_kinds":["Allocation","Interaction"],
"max_depth":2,
"max_objects":200
}
```
The query response is `{"revision":"3","elements":[...],"relations":[...],"truncated":false}`. Root IDs must exist. Traverse with a visited set; preserve distinct parallel relation IDs and process step occurrences. Deterministic UUID ordering, depth at most 4, at most 100 roots, and total returned objects at most 1,000. If the bound is reached, return `truncated: true` explicitly; never imply a complete graph. All returned relation endpoints must be included; count closure endpoints against the same bound.
List filters are conjunctive. Exact alias matching follows the V2 core's alias rules; `q` is a documented Unicode-lowercase substring match on display name, with no stemming or regex. Model cursors encode resolved revision, normalized filters, and last UUID; subsequent pages read that immutable revision even if head changes. Reject mismatched cursor/filter combinations. Project catalog pagination uses a retained catalog sequence embedded in its cursor, updated atomically with creates/renames/tombstones, so concurrent catalog changes cannot silently skip records.
### Transaction contract and example
Use a closed tagged command DTO (`op`), versioned with the API. Its initial command families are create/edit/delete/reidentify element or relation, edit project, set/reorder process steps, replace model, and the V2-defined artifact/trace commands. Explicit UUIDs and persistent aliases make multi-object AI output deterministic. Creation fails on an existing identity; editing fails on a missing identity; reference maintenance follows the shared core. `replace_model` cannot be mixed with other model commands in the initial API, but may carry service provenance.
```http
POST /api/v1/projects/6f7162cc-094c-4f30-9b70-3f6f355f0c7f/transactions HTTP/1.1
Authorization: Bearer <token>
If-Match: "p-6f7162cc-094c-4f30-9b70-3f6f355f0c7f-r1"
Idempotency-Key: 8ac64e31-2655-4120-a3b4-9cb9297d3838
Content-Type: application/json
{
"message":"Describe assessment work",
"origin":{"tool":"engineering-assistant","run_id":"run-0042"},
"commands":[
{
"op":"create_element",
"element":{
"id":"1b37b9d7-9a95-4a51-8707-010878a82419",
"alias":"assess",
"layer":"oa",
"kind":"Activity",
"name":"Assess assistance",
"description":"Determine the assistance needed.",
"position":{"x":360,"y":70},
"steps":[]
}
}
]
}
```
The alias/layer fields illustrate the proposed V2 record; final field spellings must come from its normative schema. A successful response is `200` with the new ETag and `{"revision":"2","changed":true,"created_ids":["1b37b9d7-9a95-4a51-8707-010878a82419"],"issues":[...]}`. An unallocated activity is an engineering warning, not a failed transaction. Return affected/deleted IDs and identity remappings where relevant.
Apply the batch to one candidate in order, then run structural validation once. Intermediate states may be incomplete; the final candidate must be valid. A failed command or final validator leaves the current snapshot, history, aliases and traces unchanged. `dry_run` executes the same reducer and validation, returning effects and the base revision; it neither reserves IDs nor authorizes a later write. Commit always checks head again. Shared desktop `Session` should call this reducer and keep one undo entry for a batch, rather than the server constructing a chain of independent sessions.
Transition preview stores `plan_id`, resource, base revision, normalized input, generated IDs/aliases, the exact typed commands, a plan hash, creation principal, and a short expiry (24 hours). Only the creating principal may retrieve/commit it in the first release. Return the core's explicit source/target layer transformation and conflicts; do not let the HTTP layer invent architecture elements. Commit requires the plan's original base ETag and applies the stored commands, not a regenerated plan. A concurrent edit is `412`; an expired plan is `410`; the client must preview again. Revalidate constraints at commit. An exact committed retry returns its receipt even after preview expiry. Remove expired uncommitted previews during bounded maintenance.
## Artifact, AI and automation traceability
Keep artifacts and trace links in `ProjectV2` and its common typed command set. The service must not maintain a parallel hidden engineering model. A useful minimum artifact record has UUID, persistent alias if the V2 model requires one, title, kind, media type, immutable content digest, optional external URI, and provenance (`tool`, version, run ID). The authenticated principal and server timestamp are trusted audit fields; client-supplied generator strings are attributed claims.
A trace identifies an artifact, a typed predicate such as `generated_from`, `documents`, or `verifies`, and a model anchor `{service_id, resource_id, revision, object_id}`. Permit project-level anchors explicitly. The core defines which predicates are valid; arbitrary extension dictionaries must not replace typed semantics. V2 trace representation must distinguish **live references**, which follow core identity/deletion rules, from **historical anchors**, which resolve an immutable snapshot and must not be rewritten when head changes.
For a report generated from revision `"3"`, an illustrative trace payload is:
```json
{
"op":"create_trace_link",
"trace":{
"id":"e092799d-9534-46a8-89be-f38a17fd4ce7",
"artifact_id":"bd733c59-81ae-4859-a983-147114c032cb",
"predicate":"generated_from",
"anchor":{
"service_id":"2767a28b-854f-499a-9a46-7f276f763ad1",
"resource_id":"6f7162cc-094c-4f30-9b70-3f6f355f0c7f",
"revision":"3",
"object_id":"1b37b9d7-9a95-4a51-8707-010878a82419"
}
}
}
```
The artifact and its traces can be created in the same batch. A documented `revision: "result"` input may anchor a newly created object to the resulting commit; resolve it to the exact numeric revision before persistence. New local trace commands must resolve every anchor against its nominated snapshot (or the resulting candidate). Keep initial locally created anchors within one hosted project; cross-project linking needs its own transaction/deletion policy later. Current-model navigation may report an object as changed/missing/reidentified while retaining the original historical evidence. Never retarget old evidence silently.
Import is a distinct case: an exported model may retain historical anchors to its original service/resource, while model interchange does not include that service's revision history. Preserve these anchors exactly as external evidence; report `unresolved_external_anchor` as a warning when their source is unavailable, and never label them locally verified or automatically retarget them to the imported copy. This distinction must exist in the common V2 schema/validator. Lossless model interchange preserves the reference; full evidence-history recovery requires the service backup. A replacement containing a newly forged local anchor to a nonexistent revision/object is still an error. Test import/re-export of traced models explicitly.
The minimum service stores metadata and a digest, not arbitrary binary uploads or generation jobs. Accept documented `https`/`urn` artifact URI forms as data and never fetch them automatically. A stored digest is a client-supplied claim unless a trusted producer/upload path verifies content; expose this distinction. Do not execute scripts, dereference local filesystem paths, or dispatch tools from model content. An external automation/AI client queries a pinned revision, generates its output, validates a typed batch, commits with the original precondition, and records provenance. REST plus OpenAPI is sufficient initially; an MCP adapter can later be a thin external client of the same API. A polling client follows revision summaries and deduplicates by resource/revision; no webhook delivery guarantee is claimed.
## Storage, concurrency and failure behavior
Use one server-owned directory on a local disk, such as `/var/lib/arcadia` or `%LOCALAPPDATA%/ArcadiaStudio/server`; do not default to the OneDrive workspace. Keep `models.sqlite3` and SQLite's journal companions together. WAL is a single-host storage mode and does not support network filesystems. [SQLite WAL](https://www.sqlite.org/wal.html).
Use `rusqlite` with bundled SQLite for reproducible packaging and its backup feature. One dedicated repository thread owns the connection and services a bounded queue. Tokio handles networking; synchronous parsing, validation, serialization and SQLite work do not block its reactor. This deliberately starts with serialized repository operations for modest engineering models, allowing later profiling before adding a reader pool. Tokio documents separating blocking work, and warns that already-started `spawn_blocking` work cannot be aborted; a dedicated worker makes the commit lifetime explicit. [Rusqlite](https://docs.rs/rusqlite/latest/rusqlite/), [Tokio blocking tasks](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html).
Minimal tables are store metadata (including `service_id`), `projects(resource_id, head_revision, tombstoned, created_at)`, immutable `revisions(resource_id, revision, snapshot_json, provenance_json, created_at)`, `request_receipts(principal, key, request_hash, response, expires_at)`, `transition_plans(...)`, and catalog history/sequence. `snapshot_json` is the complete V2 model including artifacts/traces; it is authoritative. Do not create separate mutable graph tables in the first implementation. Enforce primary/foreign keys and revision uniqueness; use parameterized SQL, explicit migrations, and fail startup on an unsupported newer database.
Configure and verify `journal_mode=WAL`, `synchronous=FULL`, `foreign_keys=ON`, and a finite busy timeout. SQLite documents FULL-mode WAL commit syncing; durability still depends on a correctly functioning local filesystem/device. Never change to NORMAL/OFF without changing the durability claim. [SQLite synchronous settings](https://www.sqlite.org/pragma.html#pragma_synchronous).
One commit executes the following sequence on the repository worker:
1. Begin a write transaction; look up the authenticated idempotency receipt.
2. Load current head and check exact `If-Match`; reject tombstones except the documented history path.
3. Build one candidate with the shared reducer; validate core invariants, V2 aliases/traces, referenced historical anchors and configured quotas.
4. On a change, insert immutable revision, update head and any catalog entry. Insert the success receipt in the same transaction, also for no-ops. Mark a transition plan consumed where applicable.
5. Commit. Only then acknowledge success. If commit outcome is uncertain, close/reopen the connection and inspect the receipt before admitting further dependent work; never publish an uncommitted in-memory head.
Failures before commit roll back. A dropped HTTP connection or response deadline does not revoke an already-started transaction. If a response times out after admission, return `504 outcome_unknown` when possible, with the request key, and tell the client to retry the **same** request/key or inspect its receipt. A `404` receipt while work is in flight is not proof of rollback. Queued operations may be discarded before execution if their caller has gone away; started operations must reach a known commit/rollback boundary. Tests must prove these distinctions.
Retain revisions by default. Tombstones retain evidence and receipts. Enforce a storage quota and report a clear capacity error instead of pruning history that trace links depend on. A later retention feature needs explicit anchor-aware policy. Maintain/checkpoint WAL on the worker. Back up with SQLite's backup API or stop the service cleanly before copying its store; copying a live main database file alone is not a backup procedure. Restore into an empty service directory, run integrity checks, and validate stored models before declaring ready. [SQLite backup API](https://www.sqlite.org/backup.html).
## Deployable defaults and bounded work
Ship these initial defaults as documented configuration, then measure before increasing them:
| Boundary | Initial default |
| --- | --- |
| Bind | `127.0.0.1:8787`; explicit setting required for another interface. |
| Authentication | Bearer token required, including loopback; token file required at startup. No built-in password/account flow. |
| Principals | Small configured set of token identities with read-only or read/write role; all hosted projects share this service authorization boundary initially. |
| Body bytes | 1 MiB ordinary commands; 8 MiB source/model import/replacement/validation; reject compressed request bodies initially. |
| Work admission | 8 active model requests including buffered bodies; repository queue 16; reject overload promptly with `503` and `Retry-After`. Health routes bypass the model-work queue. |
| Deadlines | 10 seconds to receive body; 30 seconds HTTP response budget; no false promise that an admitted commit was cancelled. Reverse proxy adds header/idle/connection limits. |
| Candidate size | 8 MiB serialized model; 5,000 graph elements + relations total; 50,000 process-step occurrences; 10,000 artifact/trace records; all checked before expensive semantic validation where possible. |
| Strings and operations | Names/aliases at most 1 KiB, descriptions 64 KiB, URI 4 KiB; at most 200 typed commands per transaction. V2 semantic limits may be stricter. |
| Lists / traversal | Default page 100, max 1,000; graph depth 4, roots 100, returned objects 1,000. |
| Store | Configurable 10 GiB managed-data quota initially; refuse commits exceeding quota while retaining read/export access. Account for new revision/receipt bytes and reserve journal/maintenance headroom. |
The existing validator repeatedly scans relations, including allocation checks, so its cost is not uniformly linear. Input count limits must run before it. Measure worst cases at the chosen ceilings and add adjacency/count indexes in the shared validator if needed; do not claim a hard CPU deadline based only on a Tokio timeout.
Apply a global raw body limiter and route-specific extractor limits; Axum's default limit is local to participating extractors and can be bypassed by custom body consumption. Map all rejection paths, including middleware and unknown routes/methods, to the documented API error response. [Axum body limits](https://docs.rs/axum/latest/axum/extract/struct.DefaultBodyLimit.html).
For remote deployment, use a TLS reverse proxy and keep the server port private. A non-loopback bind should require an explicit acknowledgement of the proxy/private-network configuration and a valid token configuration; this is a startup configuration rule, not a runtime user approval step. Tokens must be randomly generated high-entropy secrets, scoped to named principals, held in files with restrictive OS permissions, and checked using a constant-time secret comparison; document rotation through configuration reload/restart. Disable CORS by default. If a browser client is later added, configure exact origins and allowed headers rather than wildcard credentials. The native Iced client does not require CORS. Redact authorization headers and model/source bodies from logs; log request ID, principal ID, route template, revision, status and duration. Never put tokens in URLs or returned OpenAPI examples.
## Clear errors
Use `application/problem+json` with `type`, `title`, `status`, `detail`, `instance`, and extensions `code`, `request_id`, optional `current_revision`, and `issues`. Each issue can carry `code`, `severity`, `object_id`, `command_index`, JSON field path, or source line/column. Do not classify errors by matching English `ModelError` strings. [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html).
```json
{
"type":"urn:arcadia:problem:revision-conflict",
"title":"Project changed",
"status":412,
"detail":"The submitted revision is no longer current. Reload and review the changes before retrying.",
"instance":"/api/v1/requests/8ac64e31-2655-4120-a3b4-9cb9297d3838",
"code":"revision_conflict",
"current_revision":"3",
"request_id":"8ac64e31-2655-4120-a3b4-9cb9297d3838"
}
```
Status mapping: `400` malformed envelope/query/UUID; `401` missing/invalid token (with `WWW-Authenticate`); `403` read-only principal attempting a write; `404` missing resource; `409` duplicate identity or conflicting retry key; `410` expired plan/deleted current project; `412` stale revision; `413` body too large; `415` unsupported media; `422` syntactically decoded but invalid model/command; `428` missing precondition; `503` overload or unavailable storage; `507` configured storage capacity reached; `504` response deadline with potentially unknown admitted-write outcome; `500` unexpected internal failure. A graph root missing from a requested existing snapshot is `422` with its ID. Invalid source syntax in a committing source request is `422` with source diagnostics. Return safe details to clients; filesystem paths and database internals belong only in restricted logs.
## Desktop integration
The simplest first integration is an explicit **Connect to server / Open hosted project** mode using an asynchronous Rust HTTP client inside Iced tasks. Preserve local-file mode and its existing `Session`; both use the same core records/commands. A hosted tab tracks service URL, resource ID, base revision, acknowledged model, and local drafts. Apply/Save sends one conditional typed transaction or complete parsed replacement. The server is authoritative for a hosted project; the desktop must not open/edit the SQLite database directly.
Keep local optimistic preview if useful, but do not mark it saved until the server acknowledges the revision. On `412`, retain the draft and show the current server snapshot plus the user's proposed edit; require an explicit revised intent rather than automatically resending a complete model over newer work. Poll project metadata with `If-None-Match` while idle or refresh on focus; no WebSocket is needed. Drag updates should coalesce into one commit on release. Local undo can formulate an inverse/replacement against the last acknowledged revision; it cannot rewind other clients' history. A restore is a new revision, not decrementing the service counter.
Do not require a background server for existing local-file users. A managed loopback sidecar can follow later, but it adds binary lifecycle, token-file/port discovery, restart and upgrade responsibilities. Initially let developers/operators start the packaged server explicitly and let the desktop connect. This provides remote service use and a testable integration boundary with less process management.
## Run, package and verification plan
The official docs inspected currently report Axum 0.8.9, Tokio 1.53.1 and rusqlite 0.40.2. These are research observations, not pins already tested in this workspace. Select compatible dependency versions in Cargo.lock, enable only needed features, and verify the workspace's declared Rust 1.90 floor or explicitly raise it; current local records show builds with 1.98.1. Axum supports the required routing, state, extractor and Tower middleware model. [Axum API](https://docs.rs/axum/latest/axum/), [Tokio API](https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html), [rusqlite API](https://docs.rs/rusqlite/latest/rusqlite/).
Proposed commands, valid only after implementing the crate/configuration:
```powershell
$env:CARGO_TARGET_DIR = 'D:/CodexBuilds/Capella2-01a06e2e'
cargo build --release --locked -p oa-server
& 'D:/CodexBuilds/Capella2-01a06e2e/release/arcadia-server.exe' --bind 127.0.0.1:8787 --data-dir 'D:/ArcadiaData' --tokens-file 'D:/ArcadiaConfig/tokens.toml'
```
```sh
cargo build --release --locked -p oa-server
./target/release/arcadia-server --bind 127.0.0.1:8787 \
--data-dir /var/lib/arcadia --tokens-file /etc/arcadia/tokens.toml
```
Package a headless Windows executable and Linux binary/container separately from the GUI. Server compilation must not pull Iced/WGPU/native GUI development packages. Run Linux under a dedicated service identity with a writable data directory and a read-only token/config file; mount persistent local storage for a container and publish only through its TLS proxy. Include configuration examples, OpenAPI, migration/backup/restore instructions, licenses and checksums. No credentials should ship in a release. Use existing build storage on D: rather than consuming the low free space on C:.
On Ctrl+C/SIGTERM, mark unready, stop admission, stop accepting connections, and drain admitted repository work before closing SQLite. Axum provides a graceful-shutdown hook, and Tokio describes signaling and waiting for task completion. Set a documented supervisor grace period (for example 45 seconds), record a timeout, and rely on tested database recovery if a supervisor must terminate a stuck process; never report that unacknowledged writes necessarily failed. [Axum graceful shutdown](https://docs.rs/axum/latest/axum/serve/struct.Serve.html), [Tokio graceful shutdown](https://tokio.rs/tokio/topics/shutdown).
Release gates:
1. Core parity: graphical/API/source/JSON routes produce equal V2 models, preserve aliases/identities/step occurrences/trace fields, and reject the same structural errors. Batch rollback, deletion, reidentification and transition plans use the common reducer.
2. HTTP contract: router tests cover auth, strict DTO rejection, every status above, media types, exact ETags, limits, cursor snapshot consistency, query truncation/endpoint closure and OpenAPI examples. Axum's official testing example uses in-process service requests; add a small real-socket suite for transport behavior. [Axum testing example](https://github.com/tokio-rs/axum/blob/main/examples/testing/src/main.rs).
3. Persistence: restart after creates/edits/artifacts/tombstones; two simultaneous writes with one ETag yield exactly one new revision and one `412`; retries before/after restart produce one commit. Inject failure around revision/head/receipt/commit and prove no split state. Test abrupt process termination, recovery, disk-full/permission errors and backup/restore. Do not infer power-loss guarantees from a normal restart test.
4. Transitions/traces: a preview is side-effect free for the model; stale/expired plans cannot commit; committed retries replay; result anchors resolve; old evidence survives deletion/identity changes; incomplete or nonexistent anchors fail atomically.
5. Capacity/failure: body limits without Content-Length, slow bodies, maximum-size adverse graphs, full work queue, request cancellation after admission, graceful shutdown under writes, and storage quota. Record measured memory and latency at the chosen defaults before promising supported scale.
6. Deployment and desktop: build/test headless on Windows and Ubuntu; connect Iced, edit, detect an external API edit without losing a draft, export/reimport, and restart the service with data intact. Run fmt, strict Clippy, core/server tests and the existing desktop regressions. Test Rust 1.90 explicitly if it remains the minimum.
This design is intentionally a single-host deployable service for modest models. Multi-tenant per-project permissions, automatic merging, active-active servers, binary object storage, webhooks, Capella interchange and a built-in AI execution runtime need separate concrete requirements and are not implied by the REST API.
+13
View File
@@ -0,0 +1,13 @@
# Original Capella runtime inspection — V2
2026-09-05, Windows, installed Capella 7.0.1. The lead engineer used native Computer Use to reopen the repaired application, import its bundled In-Flight Entertainment System example through the UI into the isolated `D:/CodexBuilds/Capella2-v2-01a06e2e/capella-workspace`, and inspect the following. The installed original sample was not edited. These are observations of Capella, not evidence of Studio V2 behavior.
- At 1009×763 the Activity Explorer provides explicit OA/SA phase tabs and engineering tasks. The diagram browser and long task labels are cramped. Maximizing to 2560×1392 exposes the full task flow and diagram list.
- System Analysis tasks include transition from operational activities; actors/missions/capabilities; functional refinement/exchanges; allocation to System/Actors; interfaces/scenarios. This is useful contextual guidance, though the many separate tool panes consume substantial canvas space.
- Opened `[SAB] High Level System Overview`: blue System/Actor containers, green functions inside, external Passenger/Aircraft/Cabin Crew/Ground Operator. Nested work makes allocation readable. Dense orthogonal edges, truncated labels, small ports and a long palette add clutter; focused views and progressive disclosure should improve this.
- Selected `Manage Video and Audio Diffusion`. Properties expose condition, mode/state availability and realized operational activities. The semantic browser shows computed allocating actor/component, incoming/outgoing exchanges, decomposition, parent and related diagrams. This independently confirms that displayed nesting and functional parent differ.
- Expanded the OA model tree: activities, capabilities, interfaces, data, roles and operational entities. Selected `Broadcast Movies`; semantic browser showed Aircraft allocation, operational process, several realizing System Functions, outgoing interactions and Cruising/Takeoff state availability.
- Opened the context menu and Transitions submenu. An OA activity offers Functional Transition, Activity to System Capability, and Activity to System Mission. Started Functional Transition: a real comparison dialog shows Synthesis, Candidate model, Resulting model, Details, Apply/OK/Cancel and 23 differences under root System Function. Cancelled without applying. Studio should show meaningful choices directly and explain dependency scope before commit.
- Navigated directly from the semantic browser to `[OAB] All Operational Activities and Entities`. Yellow activities are allocated inside participant containers; Pilot and Cabin Crew are nested in Crew. A blue highlighted operational process traverses three activities while ordinary exchanges remain thin. This is the desired separation of containment, allocation and selected behavior emphasis.
V2 adaptations: visible OA/SA switch and task context; focused hierarchy plus inspector; independent semantic parent and performer; nested containers with restrained semantic colors; selective edge emphasis; direct two-way counterpart and source navigation; explicit create/link/compare actions; transition preview with readable field changes and atomic undo. No copied assets or claims of native Capella interchange.
+57
View File
@@ -0,0 +1,57 @@
# V2 scope and tradeoffs
V2 implements a general systems-engineering workbench spanning Operational Analysis and System Analysis. The normative [language and editing contract](language.rst) defines the supported Studio profile. It does not claim full Arcadia, Eclipse Capella or SysML conformance. [Acceptance](acceptance.md), [validation](validation.md) and [current state](../harness/STATE.md) record what has actually been exercised; implementation and verification are distinct.
## Supported modeling profile
| Area | Implemented semantics and authoring |
|---|---|
| Operational Analysis | Entities, actors, roles, activities, capabilities and processes; entity/work decomposition; leaf allocation; role realization/assignment; interactions and capability/process involvement |
| System Analysis | One System of Interest, external Actors, functions, missions, capabilities and functional chains; functional decomposition independent of the system boundary; allocation, functional/boundary exchanges and mission-capability links |
| Data and interfaces | Information/material/energy/event exchange items, primitive/enum/record data types, typed fields with multiplicity/units, ports, interface contents, provided/required contracts and exchange allocations |
| Behavior | Stable ordered exchange occurrences, repeated semantic exchanges, scenario lifelines and exchange/action/state occurrences, explicit ALT/PAR/LOOP/OPTIONAL fragments and branches, pre/postconditions |
| States and conditions | Participant-owned state machines, regions, state/mode/initial/final nodes, trigger/guard/effect transitions, descriptive and linked entry/do/exit work, availability and constraints |
| Perspectives and evidence | Bidirectional counterpart creation/linking, reviewed name/description updates, many-to-many typed/manual traces, arbitrary artifact URI/revision/fragment references |
| Views and editing | Multiple independent diagrams, decomposition/allocation nesting, collapse, dragging, pan/zoom/Fit/Arrange, SVG export, model explorer, typed property controls, exact strings, UUID/alias editing, history and source navigation |
| Text and storage | Canonical `arcadia 2` language, persistent aliases/UUIDs, typed constructors, highlighting/live lint, recoverable invalid drafts, text/JSON equality, deterministic V1 migration and atomic saves |
| Automation | Rust REST service for project/source/query/validation/transaction/transition/artifact workflows, ETag preconditions, bounded requests and atomic current-revision storage |
Graphical and textual parity applies to the persistent fields of this profile. Diagram placements are model data; camera, selection, source cursor and undo history are session state. Canvas gestures need not match textual operations one for one: typed inspectors and explicit commands expose precise semantics. Full source replacement must repair dependencies in its candidate, while graphical delete/reidentify commands maintain references automatically.
## Explicit semantic choices
- OA captures operational needs; SA defines the selected system contribution. SA contains no internal logical/physical components, and Studio does not decompose SA Actors. The System may be absent while authoring, with a completeness warning.
- Work decomposition and performer allocation are independent. Only leaves have direct allocations. A parent drawn within one performer can be a computed leaf summary, not an additional allocation. Role sharing does not create several direct performers for one function.
- Nesting expresses semantic containment or the selected allocation projection. Placement movement does not change ownership or allocation. Hide and delete have different effects.
- Counterpart creation copies mapped typed content and required references; existing-target updates compare only names/descriptions against a reviewed baseline. They do not synchronize arbitrary structure, reverse-engineer operational needs or automatically overwrite the other perspective. Scope follows ownership/reference dependencies, not every incident relationship.
- Processes/chains use ordered exchange occurrences. Rich fragments belong to scenarios. Guards, expressions and state behavior text are descriptive; there is no execution engine. Mixed states and modes are permitted by this Studio profile.
- OA boundary exchanges are a simplified operational-communication representation. Capability grouping is containment, not include/extend/generalization. Data/item references remain within their layer and can have explicit transitioned counterparts.
- Stable aliases are retained; comments and formatting normalize on Apply. Artifact references retain author-provided URI/revision/fragment values without fetching or executing resources.
## Capella-inspired improvement targets
These are concrete design responses, not claims that every Capella user experiences the same problems or that this prototype fixes every downside.
| Concern | Studio response | Remaining tradeoff |
|---|---|---|
| Dense workbench and difficult onboarding | Task workspaces, OA/SA context, focused properties, guided example and explicit relation/transition builders | Broader usability studies, assistive technology and localization remain |
| Noisy architecture pictures | Nested ownership/allocation views, independent placements and collapse | Manual edge routing and very large diagram optimization remain |
| Hard-to-review model files | Readable typed source, stable aliases/UUIDs, canonical ordering and one specification | Comment preservation, semantic merges and an LSP are absent |
| Hidden mutation effects | Validated atomic commands, dependency cleanup, identity maintenance, previewed counterpart changes and undo | No distributed editing or automatic structural reconciliation |
| Platform/plugin overhead | Native Rust/Iced desktop and independent headless service | WGPU has substantial dependencies and needs a working graphics adapter |
| UI-coupled automation | Renderer-independent core and documented REST commands/queries | Desktop-hosted model editing is absent; transfer uses import/export |
## Remaining boundaries
- Logical and Physical Architecture, deployment semantics, executable behavior, complete SysML or Capella metamodel support, and native `.capella`/`.aird` import/export.
- Advanced capability include/extend/generalization and complete Capella communication/scenario variants beyond the specified Studio profile.
- Autosave/crash recovery, recent-project management, semantic diff/merge, comment-preserving source edits, completion and language-server tooling. Local external-file checks do not provide a cross-process transaction lock.
- Direct desktop connection to hosted models, collaborative editing, server undo/history, retry receipts, multiple service replicas and network-synchronized data directories. The service stores current models under explicit capacity limits.
- Editable edge routing, production-scale performance guarantees, full screen-reader/keyboard diagram support, high-contrast/localized experiences and signed installers.
- Runtime plugin loading or an extension ABI. External tools can use the REST API; additional in-process infrastructure needs a concrete use case.
Windows and Linux builds are supported. Documented Docker/systemd deployment recipes are available for the service, but a recipe is not evidence of a running remote deployment. Platform, package and GUI verification must be reported from the acceptance record rather than inferred from source or CI configuration.
## Method evidence
The [semantics research](research-v2-semantics.md), [architecture research](research-v2-architecture.md) and [original Capella UI observations](research-v2-ui.md) distinguish method guidance, inspected Capella behavior and Studio design decisions. Their primary-source links are informative; `language.rst` is the concrete contract for this implementation. No Capella source code, icons or branding assets were copied into Studio.
+109
View File
@@ -0,0 +1,109 @@
# Arcadia Studio server
`arcadia-server` is a headless Rust REST service for the same schema-2 model, commands, source language, validation, artifacts and OA/SA traces used by the desktop. It stores each current project in an atomic JSON envelope. The service provides a deployment artifact and configuration examples; this repository does not assert that a remote instance has been deployed.
The complete HTTP contract is [api.rst](api.rst); machine-readable schemas are in [openapi.json](openapi.json), also served at `/api/v1/openapi.json`. The original [service research](research-v2-service.md) discusses broader alternatives; retained history, SQLite, idempotency receipts and stored transition plans from that research are **not** the implemented storage contract.
## Build and run
Build only the server to avoid GUI/renderer dependencies:
```powershell
cargo build --release --locked -p oa-server
.\target\release\arcadia-server.exe
```
```sh
cargo build --release --locked -p oa-server
./target/release/arcadia-server
```
The default bind is `127.0.0.1:8787`. Default data storage is `%LOCALAPPDATA%/ArcadiaStudio/server` on Windows and `$XDG_DATA_HOME/arcadia-studio/server` or `~/.local/share/arcadia-studio/server` on Linux. Specify `--data-dir` for deployment, and use a local disk with adequate free space. The working OneDrive directory is not the default database location. `--help` and `--version` do not open the store.
Supported options are `--bind IP:PORT`, `--data-dir PATH`, and `--token-file PATH`. `ARCADIA_SERVER_TOKEN` is an alternative to the token file; when both exist, the file wins. Tokens must have at least 32 printable ASCII characters without whitespace. Trailing CR/LF in a token file is removed. Tokens are never printed. Generate a random secret and restrict its file permissions to the service account.
Loopback permits unauthenticated native clients unless a token is configured; unauthenticated requests must use a localhost or loopback-IP Host header. Binding any non-loopback IP requires a token. A configured token protects every model route; only health and OpenAPI are public. Requests carrying a browser `Origin` are rejected because the initial service has no browser/CORS integration. Artifact URIs are stored as model data and are never fetched or executed.
## First API call
With the loopback server running:
```sh
curl --fail-with-body http://127.0.0.1:8787/health
curl --fail-with-body -i -X POST http://127.0.0.1:8787/api/v1/projects \
-H 'Content-Type: application/json' -d '{"name":"Harbor assistance"}'
```
The create response returns a service `resource_id`, revision and `ETag`. Fetch `/api/v1/projects/{resource_id}` to obtain the full `{resource_id, revision, model}` envelope. Use the exact ETag as `If-Match` on updates. With authentication enabled, add `Authorization: Bearer <your-token>`; avoid putting the token in URLs, shell history or shared example files.
The service resource ID is distinct from the editable model ID. Model IDs, aliases, diagrams, artifact URIs/revisions and trace records round-trip through source/JSON. Imported copies receive new resource IDs. An artifact's `revision` is an opaque external-artifact revision string; it is not a retained server model revision.
## Storage and concurrency
The data directory contains `.arcadia.lock` and one `<resource-id>.json` file per hosted project. Each envelope has exactly `resource_id`, positive `revision` and `model`. The process keeps an exclusive OS file lock on the directory lock file for the entire store lifetime. A second process using the same directory fails at startup. Do not directly edit, synchronize or replace files while the service is running.
Startup validates every committed envelope, its filename/UUID, revision, size and complete model. Any corrupt or unsupported envelope stops startup; the service does not silently skip bad data or invent an empty replacement. Sibling `.arcadia-write-*` files from an interrupted pre-commit write are not committed models and are ignored on startup.
Parsing, validation and blocking disk operations run on blocking workers. A shared store mutex captures snapshots and serializes final revision checks and writes. Parsing, semantic validation and query processing use the captured snapshot outside that lock; an expensive candidate does not block the catalog or another model's preparation. Commit rechecks the exact ETag, so a candidate prepared against a superseded snapshot returns `412 revision_conflict`. A successful changed update increments revision once; an unchanged model returns `changed: false` and retains its revision. Each write serializes the whole envelope to a sibling temporary file, syncs it, and atomically replaces the destination. Unix also syncs the directory. The in-memory snapshot changes only after the replacement succeeds. A directory-sync error after replacement is reported as an uncertain write outcome while the in-memory head still matches the installed file.
Atomic replacement protects against partially written envelopes. It does not promise survival of every device/filesystem power-loss failure, protect against out-of-band edits, or coordinate OneDrive/network filesystems. The storage directory must be local to this one process. DELETE permanently removes the envelope; there is no recycle bin, tombstone or server undo history.
The service retains **only the current revision**. Pagination can supply the returned `revision`; if head changes between pages, it returns `409 snapshot_changed` so the caller can restart. Project-catalog pagination is live UUID-order pagination; concurrent creates/deletes may change later pages. There is no request receipt/idempotency ledger. Never automatically retry a model-creating request after an uncertain connection failure. Reload the project after an uncertain conditional write; the original ETag cannot overwrite an accepted newer revision.
Back up by stopping the server cleanly and copying committed JSON envelopes to a separate location. Restore into an empty directory, start the service, and check readiness and representative exports. This preserves current models and revisions, not earlier revisions. Do not treat a live directory copy as a consistent multi-project backup. Keep backups outside the managed data directory.
## Limits and errors
- Raw request bodies: 8 MiB, including requests without `Content-Length`. Encoded/compressed request bodies are rejected. The same limit applies to serialized model JSON; the storage envelope permits small framing overhead.
- At most eight admitted model requests and eight active/waiting blocking workers. Overload returns `503` with `Retry-After: 1`. A 30-second request deadline bounds body receipt and response waiting. A started blocking write can finish after that deadline; `504 outcome_unknown` means reload before retrying.
- At most 5,000 elements plus relations, 20,000 total persistent model records, and 50,000 ordered step references per model. Graph requests allow 1–100 roots, depth 0–4, and at most 1,000 returned objects. List pages contain 1–1,000 records, default 100.
- Source declarations and decoded JSON records are counted before structural validation. V1 import includes the occurrences and placements migration will generate in its early budget check. These limits apply to import, source/model replacement, candidate validation and full-model transaction commands. Over-budget candidates cannot monopolize the catalog while being rejected.
- Transactions accept at most 200 commands, including nested batch wrappers. All commands apply to a candidate, then shared validation accepts or rejects the batch atomically. Dry run returns the candidate and diagnostics without writing.
- The fixed store cap is 128 hosted models and 256 MiB of serialized envelopes. New/imported/replaced models that exceed it return `507 store_capacity`. Startup enforces the same limits. Delete/export unneeded projects before adding more data; there are no hidden eviction or pruning rules. The server stores all current models in memory; memory includes Rust object overhead and in-flight candidates in addition to serialized bytes.
Errors use `application/problem+json` with stable `code`, HTTP `status`, human `detail`, and contextual `issues` when available. Responses carry `X-Request-ID`; configured bearer credentials and source/model bodies are not logged. The core currently supplies human command errors; the API maps them to `422 command_rejected` without parsing English messages into invented machine codes. Source errors preserve line/column diagnostics.
## Linux deployment
`deployment/Dockerfile` builds a headless image from the repository root:
```sh
docker build -f deployment/Dockerfile -t arcadia-server:local .
docker run --rm --name arcadia-server \
-p 127.0.0.1:8787:8787 \
-v arcadia-data:/data \
--mount type=bind,src=/absolute/path/arcadia-token,dst=/run/secrets/arcadia-token,readonly \
arcadia-server:local
```
The token file must be readable by UID 10001 inside the container. The default container binds `0.0.0.0` internally and therefore requires the token. Publishing only to host loopback keeps it behind a host TLS reverse proxy. Keep the volume on local storage; do not run multiple replicas against the same volume.
For a native Linux installation, create an `arcadia` service account, install the release binary at `/usr/local/bin/arcadia-server`, put the restricted token file at `/etc/arcadia/token`, and install [arcadia-server.service](../deployment/arcadia-server.service). The unit's `StateDirectory` creates `/var/lib/arcadia` for that account. Inspect the unit and paths before enabling it with systemd. It binds loopback; expose it remotely through a TLS reverse proxy with request-header, connection and idle-time limits. No TLS endpoint or certificate provisioning is built into the binary.
Ctrl+C/SIGTERM stops admission and drains HTTP requests. Already-started blocking tasks run to their completion boundary. The systemd example permits 45 seconds before a supervisor kill; an interrupted process recovers by loading the last installed envelopes. `/health/ready` means the initial store validation succeeded and the process can respond; it is not an ongoing disk-free-space or integrity probe.
## Desktop and automation integration
A desktop or automation client can query current JSON/source, edit with shared typed commands, validate/dry-run, and commit using the original ETag. For hosted editing, keep the user's draft until the service acknowledges the new revision. On `412`, preserve the draft and reload server state for review; a blind replacement retry would discard another client's work.
External tools can use the artifact-link and trace query routes, and create/edit those records in the same atomic transaction as model changes. URI and external revision fields preserve the author's values; the service supplies no fetching, credentials, artifact-content verification or AI execution runtime. OpenAPI is the integration contract. An optional future adapter can call this API without introducing a second mutation path.
`POST /projects/{id}/transitions/preview` produces a shared core transition plan and `base_revision` without storing a plan or changing the model. Send that response to `/transitions/apply` with the original ETag. Apply checks the server revision and the core's model-change fingerprint, preserves planned IDs, validates the candidate, and commits once. Plans with unresolved conflicts cannot apply. A submitted plan is a client-owned command proposal, not a signed server authorization; a client able to apply a plan already has the same write permission as the transaction endpoint.
## Verification
```sh
cargo fmt -p oa-server -- --check
cargo clippy -p oa-server --all-targets --locked -- -D warnings
cargo test -p oa-server --locked
```
The HTTP integration suite starts real loopback listeners over isolated temporary stores. It covers competing conditional writes, stale and nonexistent deletion, restart persistence, source/JSON equality, dry-run and no-op behavior, invalid candidates, graph bounds, authentication, body limits, startup corruption/locking, capacity, transition revision checks, artifact persistence and a forced replacement failure. It also checks prompt rejection of over-budget source/native/legacy imports and replacements, catalog responsiveness during candidate preparation, and a stale prepared candidate losing to a newer HTTP edit. A deterministic store test checks the final revision comparison with two fully prepared candidates. Passing those tests does not by itself prove power-loss behavior, external reverse-proxy configuration, container execution or remote deployment. Platform/release results must be recorded only after running their checks.
Verified on 2026-09-05:
- Windows R1/R2/R4 source/API integration: all 16 current HTTP/configuration tests plus the deterministic commit test pass; strict server all-targets Clippy passes. New cases cover mission maps, branching/reused chains, typed call value edits, original V2 compatibility, source/JSON equality and persistence after restart. See `harness/release-language-integration.md`; native UI and remaining release semantics are separate gates.
- Windows R2 release checkpoint: all 14 server integration/configuration tests plus one deterministic commit test pass; formatting and strict all-targets Clippy pass. A separate optimized executable probe rejects the previously stalling 12,000-activity request in 0.050 seconds; concurrent JSON import and accepted-source preparation leave catalog reads responsive. See `harness/release-text-service-audit.md` for exact measurements and scope.
- Ubuntu 24.04 under WSL, prior V2 baseline: all 12 then-existing tests passed using native Linux processes and temporary filesystem stores; strict all-targets Clippy and the optimized build passed with the locked, vendored dependency set. A separate release-executable smoke run verified bearer authentication, non-loopback token enforcement, exclusive directory locking, conditional create/update/delete, update and deletion persistence across restarts, served OpenAPI, and clean SIGTERM shutdown. The new first-release changes still require native Linux re-verification.
- `systemd-analyze verify` passes after copying the unit to a native temporary directory with mode 0644 and substituting the built executable's path for its documented installation path. The service account, token installation and system-wide service activation were not performed.
- A Docker daemon was unavailable, so the Docker image was not built or run. No remote service or TLS proxy was deployed. The declared Rust 1.90 minimum and Docker builder were not independently compiled in this verification; the available stable toolchains built the locked dependencies successfully.
+70
View File
@@ -0,0 +1,70 @@
# V2 integration interface — frozen core skeleton
`crates/oa-core/src/model.rs` now contains the exact public schema-2 declarations. This file records integration contracts; Rust declarations are authoritative. All new model fields are required in schema-2 JSON; schema-1 migration constructs them explicitly.
- `Project {schema_version,id,alias,name,description,elements,relations,diagrams,artifact_links,traces}`. `Project::empty()` gives empty maps and no invented SA system. `SCHEMA_VERSION = 2`.
- One global UUID and alias namespace includes project, elements, relations, diagrams, diagram nodes, artifact links and traces. Helpers: `valid_alias`, `reserved_alias`, `Project::identities`, `contains`, `fresh_alias(name,fallback,id)`.
- `Element {id,alias,name,description,parent:Option<Id>,body:ElementBody}`. `ElementBody::Oa(OaBody) | Sa(SaBody)`. Kind/layer are derived through `.kind()` / `.layer()`; no `.kind` or `.position` fields remain.
- `OaBody`: Entity, Actor, Role, Activity, Capability, Process{steps}, Shared(SharedBody). `SaBody`: System, Actor, Function, Capability, Mission, FunctionalChain{steps}, Shared(SharedBody).
- `SharedBody`: ExchangeItem, DataType, DataField, Interface, Port, Scenario, Lifeline, Occurrence, StateMachine, Region, State (kind distinguishes State/Mode/Initial/Final), Constraint. Payloads and enum spelling are in model.rs. Common `.shared()`/`.shared_mut()` and `.steps()`/`.steps_mut()` accessors support property forms.
- `steps` contain occurrence ELEMENT IDs, never relation IDs. An occurrence is parented by its process/chain/scenario/branch. Its Exchange payload references a relation. Lifelines are elements parented by a scenario. StateMachine -> Region -> State/Mode/Initial/Final uses parent ownership, with optional nested regions under state/mode.
- `Relation {id,alias,name,description,body:RelationBody}` exposes `.kind()`, `.source()`, `.target()`. Body exposes `.endpoints()`, `.set_endpoints(a,b)`, `.items()`. `RelationBody::new(kind,source,target)` creates empty/default payload fields.
- V1 Contribution maps to `CapabilityInvolvement{source,target}`; V1 Realization to `ChainInvolvement{source,target}`. Allocation/Interaction retain spelling; Interaction includes an `items: Vec<Id>` field.
- `Diagram {id,alias,name,description,layer,projection,nodes}`; `ProjectionKind`: Overview, Decomposition, Allocation, Traceability. Traceability accepts both layers; other views are layer-filtered. `NodePlacement {id,alias,element,position,size,collapsed}` has no description or parent. Absolute coordinates, positive finite size. At most one placement per element per diagram.
- `ArtifactLink {id,alias,name,description,owner,relation,uri,revision:Option<String>,fragment:Option<String>}`; owner is any existing model object. Preserve URI/revision verbatim; never fetch during validation.
- `Trace {id,alias,name,description,oa,sa,kind,baseline:Option<TraceBaseline>}`. Canonical OA and SA endpoints can reference elements or relations. Baseline contains `oa` and `sa` `TracedProperties{name,description}` only, no live UUID references.
## Reference-valued fields
- Element.parent; process/chain/scenario.steps; occurrence Branch.occurrences; Fragment.branches.
- DataField.type_ref only when `TypeRef::Named(Id)`.
- Interface.items; Port.interface; Scenario.context; Lifeline.represented.
- Occurrence Exchange.exchange/sender/receiver; Action.action/lifeline; State.state/lifeline.
- Constraint.constrained.
- All RelationBody.source/target; exchange variants.items.
- NodePlacement.element; ArtifactLink.owner; Trace.oa/sa.
- Map keys, all record `id` fields and diagram-node IDs are identity fields, not ordinary references.
`Option<Id>` uses None for incomplete drafts; warnings explain missing references. Existing-but-wrong-kind or dangling Some values are structural errors. Ordered vectors preserve order and repeated use through distinct occurrence IDs. No dual ownership fields exist.
## Command/session API
Commands derive Clone/Serialize/Deserialize. Public `execute_batch(&Project, &[Command]) -> Result<Project, ModelError>` clones, applies all operations, validates once, and returns the accepted candidate without mutating input.
Core mutation commands: Replace(Project), CreateElement(Element), EditElement(Id,Element), CreateRelation(Relation), EditRelation(Id,Relation), CreateDiagram(Diagram), EditDiagram(Id,Diagram), CreateArtifact(ArtifactLink), EditArtifact(Id,ArtifactLink), CreateTrace(Trace), EditTrace(Id,Trace), CreateNode{diagram,node}, EditNode{diagram,original,node}, RelocateNode{node,diagram}, EditProject{id,alias,name,description}, Reparent{element,parent}, Reidentify(Id,Id), Rename(Id,String), Describe(Id,String), SetAlias(Id,String), Delete(Id), Batch(Vec<Command>).
`RelocateNode { node: Id, diagram: Id }` moves the existing placement between diagrams without deleting it or pruning references. Identity, alias, geometry, collapse state and attached artifacts survive. The transaction rejects duplicate element placement or incompatible target layer. Relocation into the same diagram is a no-op. `Reparent` to the same parent is also a no-op, preserving ordered occurrence/branch membership and history.
Convenience commands: Add(Kind), AddElement{kind,layer,name,parent}, Connect(RelationKind,String,Id,Id), AddStep(sequence,exchange), RemoveStep(sequence,index), MoveStep(sequence,from,to), SetSteps(sequence,Vec<Id>), MoveNode{diagram,node,position,with_descendants}, Layout(diagram).
`Session::try_new(Project)->Result<Session,ModelError>`, `Session::new(Project)->Session` (validated convenience, panics on invalid input), `project()->&Project`, `revision()->u64`, `execute(Command)->Result<Option<Id>,ModelError>`, `execute_many(&[Command])->Result<(),ModelError>`, undo/redo/dirty/mark_saved/can_undo/can_redo. Snapshot history remains bounded. Every changed accepted mutation/undo/redo increments revision; no-op or failed command does not.
Transition types/functions are documented below. No request receipts, transport versions, or server infrastructure belong in core.
P1 extension to frozen relation interface: FunctionalExchange now has `source_port: Option<Id>, target_port: Option<Id>` in addition to source/target FUNCTION IDs and items. RelationBody adds `ExchangeAllocation{source,target}` (boundary-exchange relation → same-layer interaction/functional-exchange relation), `InterfaceProvision{source,target}` and `InterfaceRequirement{source,target}` (participant/port → Interface), and `StateAction{source,target,phase:StateActionKind}` (State/Mode → Activity/Function; Entry/Do/Exit). These fields participate in reference maintenance. `.permits(Kind,Kind)` is a convenience only; ExchangeAllocation requires relation-ID-aware validation/candidate queries.
## Transition API (implemented)
`transitions::plan_transition(&Project, &TransitionRequest) -> Result<TransitionPlan, ModelError>`; `apply_transition(&Project, &TransitionPlan) -> Result<Project, ModelError>`; `trace_drift(&Project,&Trace)->Drift`; `project_fingerprint(&Project)->String`.
`transitions::counterpart_kinds(source: Kind, direction: TransitionDirection) -> Vec<Kind>` is the shared choice policy for UI and other clients. It returns supported explicit element counterpart kinds in stable `Kind::ALL` order using the same compatibility predicate as transition validation; an empty list means no automatic element mapping.
`TransitionRequest {direction:OaToSa|SaToOa, sources:Vec<Id>, scope:SelectedOnly|Required|Subtree, mappings:Vec<TransitionMapping>, fields:Vec<TransitionField>, resolutions:Vec<FieldResolution>, destination_diagram:Option<Id>}`. `TransitionRequest::new(direction,sources)` defaults to selected-only and name/description fields. `TransitionMapping {source,target:Option<Id>,target_kind:Option<Kind>}` selects reuse or explicit creation kind. An explicit mapping with target=None means create a new counterpart even if another trace exists. Without mapping, one existing trace is reused; multiple traces require selection.
`FieldResolution {source,field:Name|Description,decision:TakeSource|KeepTarget|Value(String)}`. `TransitionPlan {base_fingerprint,request,changes,conflicts,warnings,commands}`. Changes carry source/target IDs, Create/Update/Reuse and before/after field changes. Conflicts have optional source/target/field and message. Conflicts prevent apply. The deterministic fingerprint guards stale plans, is not a security hash, and does not introduce persistent project revision fields. Planned IDs and aliases remain unchanged during apply. Server owns request idempotency/revision storage. Apply is one candidate transaction; Session adoption can use Command::Replace(candidate) for one Undo.
OA Capability defaults to SA SystemCapability; explicit OA Capability to SA Mission is supported with `TraceKind::Manual`, and SA Mission defaults to OA Capability. Repeated previews preserve reviewed divergent name/description values when the source has not changed. Source-only changes propose an update; different edits on both sides require review. An explicit decision records both accepted values. Target-only edits do not advance the skipped field's baseline, preserving reverse propagation.
## Projection API (implemented)
`projection::project_view(&Project,diagram_id)->ProjectedView {nodes,edges}`. `ProjectedNode {id,element,position,size,visual_parent:Option<Id>,depth,collapsed}`; id/visual_parent are placement IDs. `ProjectedEdge {id,source,target,trace}` uses placement endpoint IDs. `descendant_nodes(&Project,diagram,node)->Vec<Id>` includes collapsed descendant placements, so moving a container also keeps their future expanded positions consistent.
## Final integration notes
- `validation::can_parent(project,kind,layer,parent)`, `can_connect(project,kind,source,target)`, `effective_performer(project,work)`, and `allocation_summary(project,work)` are public. The latter two derive information and never materialize allocations. Shared roles are valid; ambiguity and role/direct-allocation mismatch are warnings. One direct performer per leaf is structural.
- Owned state machines require participant owners; an unowned state machine is valid incomplete work with a warning, admitted by `can_parent`. Functional port ownership/direction, scenario lifeline contexts, state/action ownership, interface item contracts and boundary/work exchange allocations share core validation.
- Projection expands container bounds around descendants using 24-unit side/bottom padding and 44-unit header space. Persisted positions/sizes remain editable requested anchors/minima. Layout recursively places descendants and resizes their containers. Allocation projection suppresses the redundant allocation edge.
- `mark_saved(snapshot: Project)` keeps the explicit snapshot signature. Core accepted project state is private.
- Existing counterpart updates are intentionally scoped to reviewed name/description fields. New selected objects/dependencies copy mapped typed bodies. Existing structural bodies are not automatically overwritten; they remain editable using ordinary commands/source. Trace drift warnings use the same name/description baselines. Many-to-many trace records are supported, while ambiguous counterpart or conflicting merge choices require explicit transition selections.
- `Project::demo()` now supplies 39 semantic elements, three focused diagrams (OA allocation, SA allocation, SA decomposition), ten reviewed traces, and two external code/CAD artifact links. It covers information/material/energy items, typed fields, interfaces/ports, repeated scenario messages, a process/chain, normal/degraded modes, guarded transitions, availability, a typed state entry action, and a constraint. Its validator output is empty.
+49
View File
@@ -0,0 +1,49 @@
# V2 validation record — 2026-09-05
Arcadia Studio 0.2.0 was built and exercised on Windows and native Ubuntu 24.04.2/WSLg, using Rust 1.98.1 and Iced 0.14. Historical V1 reports are not counted as V2 evidence. Remote CI and the declared Rust 1.90 minimum were not executed.
## Automated evidence
| Area | Observed result |
|---|---|
| Windows final source | All 93 tests pass: 56 core, 25 desktop and 12 actual HTTP/storage tests. Workspace formatting, strict all-target Clippy and optimized desktop/server build pass. |
| Native Linux | Full 91-test baseline and strict workspace Clippy passed, then both subsequent desktop-only fixes passed the latest 25 desktop tests and strict desktop all-target Clippy. Together these native runs cover the current 93 tests; a final full-workspace 93-test invocation was not performed. Formatting and final optimized desktop/server build pass. |
| Core invariants | Typed whole-model equality, deterministic V1 migration, exact strings/coordinates, strict input, atomic failure/undo, all-record identity/deletion, nested projection, roles/allocations, scenarios/states, reviewed transition baselines, Mission mapping and placement relocation. |
| Desktop behavior | Lossless graphical drafts, typed reference choices, constructor preservation, shared perspective and placement edits, modal guards, invalid save/source recovery, counterpart focus/decision context and history. Behavior/Data authoring preserves architecture while updating ordered semantic occurrences. |
| Service | Real HTTP ETags/concurrent writes, restart, invalid candidates, dry runs, bounded queries, body/auth/Host limits, exclusive store lock/capacity and transition/artifact/source workflows on Windows and Ubuntu. |
| Contracts and examples | Both reST contracts render to HTML5 with Docutils report/halt level 2 and no warnings. V2 text/JSON examples compare exactly with zero diagnostics: 39 elements, 23 relations, 3 diagrams, 10 traces and 2 artifacts. V1 examples are explicitly named migration fixtures. |
A stale legacy-fixture include path initially failed a final test invocation. Both references were corrected and the complete suite rerun successfully. No compiler warning or failed check is being treated as a passing result.
## Actual native interface evidence
The lead's [native acceptance record](../harness/verification-v2-native.md) and fresh [independent Windows review](../harness/review-v2-final-ui.md) document actual Computer Use, not screenshots inferred from source.
- Lead Windows: original repaired Capella OA/SA/transition inspection; Studio source colors/live located lint/rejected Apply/Revert; process, repeated scenario and state navigation; new/empty model; OA→SA creation and explicit reverse creation/undo; modal background guards.
- Lead Windows: opened actual REST-edited source, observed changed project/function/CAD fields, edited CAD revision and saved. External file modification then caused a visible save refusal with external bytes preserved.
- Fresh Windows reviewer: graphical fragment→branch→exchange authoring with a quoted guard and filtered references; saved model; minimum 1100×700, default 1440×900 and 2560-wide layouts; state-route selection/details and 150% nested typography on rebuilt executables.
- Lead Linux: actual file dialog/save/reopen, shared Energy perspective edit, explicit manual trace, diagram layer changes, placement identity/drag/relocation and artifact ownership. Continuous merged-text input, Keep target, target-change invalidation, reviewed commit and reverse preview were operated after fixes.
- Final packaged Windows: independently created a scenario Action with function/lifeline references, saved/reopened it, and verified the architecture retained its three original function placements.
- Final packaged Linux: lead launched the exact refreshed executable, reopened that Windows-authored file, and observed its 40 elements/23 relations/zero checks, preserved nested SA architecture and rendered saved Action. Closed normally with exit 0 and no stderr.
Native Linux ran with Mesa software Vulkan:
~~~sh
WGPU_BACKEND=vulkan VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./arcadia-studio
~~~
## Independent findings and disposition
Requirements, correctness, native UI/runtime and architecture reviews were completed by agents independent of the relevant implementation. Material findings were reproduced, fixed and reverified. These included transition baseline stability and target-specific decisions, external save protection, sequence ordering and Fragment defaults, Mission mapping, graphical field parity, modal ownership, reference filtering, placement focus, behavior selection, merged-input focus and nested typography. Final focused reviews reported no new material finding.
The original [UI review](../harness/review-v2-ui-runtime.md), [correctness review](../harness/review-v2-correctness.md), [requirements/architecture review](../harness/review-v2-requirements-architecture.md) and [final regression review](../harness/review-v2-final-regressions.md) retain their intermediate observations and later dispositions. Pending items in those historical sections are closed by the later final/native records.
## Service and release evidence
The [service verification record](server.md#verification) covers optimized Windows/Linux processes, health/create/update/stale-delete/auth/store-lock/restart/SIGTERM checks and native systemd unit validation. The separate [API transfer review](../harness/review-v2-api-transfer.md) records the Windows source→REST→native desktop transfer. Docker execution, remote deployment and systemd installation were not performed. No test service or Studio acceptance window is intentionally left running.
[Linux verification](../harness/verification-v2-linux.md) records native commands and executable hashes. [Package verification](../harness/verification-v2-packages.md) records archive inventories and executable equality. Release bundles contain both desktop/server binaries, examples, rendered/source contracts, documentation, deployment recipes and acceptance records.
## Limits
Graphics adapter coverage beyond the tested Windows and Ubuntu/WSLg environment, accessibility certification and production-scale performance are untested. LA/PA, execution/simulation, full Capella/SysML conformance, native Capella interchange, autosave and direct desktop-hosted editing remain outside the [specified prototype](scope.md).