feat: preserve nested source projects across desktop and REST
This commit is contained in:
+78
-23
@@ -690,7 +690,9 @@ subnormal coordinates are retained.</p>
|
||||
<h2>3. Element declarations and typed fields</h2>
|
||||
<p>Each element declaration starts with its layer and kind, for example
|
||||
<span class="docutils literal">oa_activity</span> or <span class="docutils literal">sa_function</span>. Every element body accepts
|
||||
<span class="docutils literal">description</span> (default <span class="docutils literal">""</span>) and <span class="docutils literal">parent</span> (default <span class="docutils literal">none</span>). The table
|
||||
<span class="docutils literal">description</span> (default <span class="docutils literal">""</span>) and <span class="docutils literal">parent</span> (default <span class="docutils literal">none</span> for a
|
||||
document-level declaration, or its lexical element owner when nested; section
|
||||
6.2). The table
|
||||
lists all additional fields. <span class="docutils literal">ref</span> means <span class="docutils literal">@alias</span>; <span class="docutils literal">ref?</span> also accepts
|
||||
<span class="docutils literal">none</span>. Lists preserve order. Unknown or duplicate fields are errors.</p>
|
||||
<table>
|
||||
@@ -1450,14 +1452,18 @@ 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 <span class="docutils literal">@</span>.</p>
|
||||
<p>The grammar below uses <span class="docutils literal">*</span> for repetition and <span class="docutils literal">?</span> for optional syntax.
|
||||
<span class="docutils literal">kind</span> is exactly an element/relation keyword listed above. Typed field
|
||||
<span class="docutils literal"><span class="pre">element-kind</span></span> and <span class="docutils literal"><span class="pre">relation-kind</span></span> are exactly the corresponding declaration
|
||||
keywords listed above. Typed field
|
||||
sets and enum constructors are closed by sections 3–5.</p>
|
||||
<pre class="code text literal-block"><code>document = "arcadia" "3" ";" project
|
||||
project = "project" alias string identity? "{" project-entry* "}"
|
||||
project-entry = "description" string ";" | declaration
|
||||
declaration = (kind | "diagram" | "artifact" | "trace")
|
||||
declaration = element
|
||||
| (relation-kind | "diagram" | "artifact" | "trace")
|
||||
alias string identity? fields
|
||||
| "node" alias identity? fields
|
||||
element = element-kind alias string identity? element-body
|
||||
element-body = "{" (field-name value ";" | element)* "}"
|
||||
identity = "id" uuid-string
|
||||
fields = "{" (field-name value ";")* "}"
|
||||
value = string | number | "true" | "false" | "none"
|
||||
@@ -1469,7 +1475,7 @@ record ends with a semicolon, including fields whose values are nested records.
|
||||
Trailing list commas are invalid. Fields may appear in any order.</p>
|
||||
<p>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
|
||||
creates a fresh UUID for that parse. After Apply, accepted source includes the
|
||||
generated identity. Authors must keep identity clauses to preserve existing
|
||||
objects across subsequent edits.</p>
|
||||
<p>All declaration aliases are collected before references resolve, so forward
|
||||
@@ -1493,8 +1499,8 @@ deeply nested drafts produce a diagnostic instead of exhausting the parser stack
|
||||
root-level <span class="docutils literal">.arcadia</span> file with the source3 project header above. A project entry
|
||||
MAY also be <span class="docutils literal">include string ";"</span>. An included fragment consists only of ordinary
|
||||
declarations and include statements; it MUST NOT contain another version header,
|
||||
project wrapper, or project description. Includes MUST occur between declarations,
|
||||
never inside a typed field value. For example:</p>
|
||||
project wrapper, or project description. Includes MUST occur at document level,
|
||||
never inside an element body or typed field value. For example:</p>
|
||||
<pre class="literal-block">arcadia 3;
|
||||
project rescue "Rescue" id "00000000-0000-4000-8000-000000000001" {
|
||||
description "Operational model";
|
||||
@@ -1514,7 +1520,7 @@ spelling; filesystems do not participate in resolution.</p>
|
||||
includes. Include cycles are errors with their chain and including location.
|
||||
Missing files, duplicate JSON file keys and supplied unreachable files are
|
||||
errors. The compiler MUST NOT read local files, network resources, environment
|
||||
variables or the process working directory. A future native loader is responsible
|
||||
variables or the process working directory. The native loader is responsible
|
||||
for translating a directory into this explicit bundle and enforcing physical
|
||||
path ownership; the pure compiler grants no filesystem access.</p>
|
||||
<p><span class="docutils literal">inspect_includes(path, source, is_entry, limits)</span> supports native discovery
|
||||
@@ -1547,8 +1553,8 @@ input. The returned source index and diagnostics address the returned stabilized
|
||||
bytes. Clients MUST retain that bundle after successful Apply; recompiling the
|
||||
original buffers with missing identities would generate fresh identities.</p>
|
||||
<p>Bundle diagnostics wrap the existing Diagnostic with a logical path and optional
|
||||
related locations. Declaration spans, top-level field value spans, project
|
||||
header/body/description spans and resolved include locations are available in
|
||||
related locations. Declaration spans, own-field value spans, lexical owners,
|
||||
declaration bodies, project header/body/description spans and resolved includes are available in
|
||||
the source index. Columns remain one-based Unicode scalars within the real file;
|
||||
spans never refer to concatenated or synthetic source.</p>
|
||||
<p>Single-string parse/lint APIs remain filesystem-free and reject include
|
||||
@@ -1557,6 +1563,36 @@ continue through their existing migration readers and reports. Modular projects
|
||||
require source3; migration is performed before modular export, not independently
|
||||
on included fragments. Modular source does not change the semantic JSON schema.</p>
|
||||
</section>
|
||||
<section id="nested-element-ownership">
|
||||
<h3>6.2. Nested element ownership</h3>
|
||||
<p>An element body MAY contain element declarations interleaved with its ordinary
|
||||
fields. Its immediate enclosing element is its lexical owner. For example:</p>
|
||||
<pre class="literal-block">sa_actor harbor "Harbor operator" {
|
||||
sa_port radio "Radio" {
|
||||
contract { Component { kind Standard; orientation Unset; }; };
|
||||
}
|
||||
}</pre>
|
||||
<p>Nesting expresses semantic <span class="docutils literal">Element.parent</span> only. It does not allocate a
|
||||
function to an actor or System, deploy equipment, or add or reorder occurrence
|
||||
references in an owner's explicit <span class="docutils literal">steps</span> list. Those relationships retain
|
||||
their explicit records and fields. The
|
||||
same parent-kind, layer and acyclicity constraints apply as for flat source.
|
||||
Only element declarations can nest: relations, diagrams, placements, artifacts,
|
||||
traces and include directives remain at document level. A typed record value
|
||||
contains fields, never element declarations.</p>
|
||||
<p>If a nested declaration omits <span class="docutils literal">parent</span>, it inherits its lexical owner's UUID.
|
||||
An explicit <span class="docutils literal">parent</span> MUST reference that owner; another reference or <span class="docutils literal">none</span>
|
||||
is an error located at the parent value. At document level, omitted <span class="docutils literal">parent</span>
|
||||
means <span class="docutils literal">none</span> and explicit parent references retain their existing meaning.
|
||||
Nesting creates no alias namespace. Aliases and UUIDs remain globally unique,
|
||||
and references can target nested declarations before or after their definition
|
||||
or from another included file. Identity stabilization inserts missing UUIDs in
|
||||
nested headers just as it does in document-level headers.</p>
|
||||
<p>Source positions distinguish each declaration's own fields from its descendants'
|
||||
fields. A declaration's body span encloses its lexical children as well as its
|
||||
own fields. An inherited parent has no synthetic source token or field span.
|
||||
Located diagnostics and editor navigation MUST address real source bytes.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="canonical-output-and-source-tooling">
|
||||
<h2>7. Canonical output and source tooling</h2>
|
||||
@@ -1574,11 +1610,10 @@ relations; diagrams and their nodes follow, then artifacts, then traces.
|
||||
Fields use deterministic name order. Output ends with LF. Alias values come
|
||||
from the model, never from regenerating display names.</p>
|
||||
<p>Comments and input whitespace are lexical trivia to the canonical writer.
|
||||
Canonical export and the current desktop single-document Apply path do not
|
||||
preserve them; the editor MUST explain that behavior. This existing path does not
|
||||
define an acceptable lossless authoring policy for a future project editor.
|
||||
The pure project compiler in section 6.1 preserves them while inserting missing
|
||||
identities. Project update/export distinctions are described below.
|
||||
Canonical export does not preserve them. The source-project editor and compiler
|
||||
preserve authored comments and whitespace on Apply while inserting missing
|
||||
identities. Explicit canonical export remains a separate operation. Project
|
||||
update/export distinctions are described below.
|
||||
Camera position, selection, source cursor, and undo history are not stored
|
||||
model fields. Layout and collapse state inside NodePlacement are model data.</p>
|
||||
<p>The pure <span class="docutils literal">format_bundle</span> exporter uses the same typed declaration writer as
|
||||
@@ -1594,12 +1629,24 @@ For every valid P, <span class="docutils literal"><span class="pre">compile_bund
|
||||
when the caller supplies sufficient admission budgets.</p>
|
||||
<p><span class="docutils literal">update_bundle(compiled, P, limits)</span> is a core source update facility. It
|
||||
checks that the compiled document/index/model snapshot still agrees, preserves
|
||||
existing declaration file ownership and all untouched document bytes, and
|
||||
recompiles the result before returning it. New children inherit their owner's
|
||||
file; new roots use canonical module rules and add an explicit entry include.
|
||||
untouched document bytes, and recompiles the result before returning it. Flat
|
||||
declarations retain their document-level position and file when reparented.
|
||||
Nested declarations move into their new semantic owner's body, across files if
|
||||
necessary; becoming a root moves them to document level. New children prefer
|
||||
their owner's body. New roots use canonical module rules and add an explicit
|
||||
entry include.
|
||||
Removed declarations do not delete source files. Alias changes also update
|
||||
declarations containing references to that alias. Invalid or stale updates
|
||||
return errors without mutating the input workspace.</p>
|
||||
<p>A moved declaration carries its contiguous leading comment block and comments
|
||||
following its closing brace on the same line. A blank line separates a comment
|
||||
block from the following declaration; such gap comments stay in the enclosing
|
||||
scope. Comments trailing a preceding field likewise stay with that field.
|
||||
Deletion retains annotations in the nearest surviving original scope and
|
||||
separates them from adjacent declarations with blank lines. Surviving descendants
|
||||
move with an enclosing declaration unless their own semantic parent changes;
|
||||
independent parent and child moves are resolved together. Leading whitespace
|
||||
adjusts to the destination scope; other token spelling remains authored.</p>
|
||||
<p>Updates MUST NOT canonicalize surviving declarations in full. Header, field and
|
||||
nested value positions are derived from the existing syntax parser. Unchanged
|
||||
fields retain their exact bytes, including field order, omitted defaults, number
|
||||
@@ -1623,23 +1670,31 @@ that the result remains valid syntax. Unkeyed or duplicate-key lists use source
|
||||
positions. Removed-entry comments remain as annotations in the list; newly added
|
||||
entries receive formatted syntax. These rules preserve comments without silently
|
||||
attaching new semantic identity to an ambiguous list entry.</p>
|
||||
<p>Retained declaration UUIDs and source ownership remain unchanged. Records match
|
||||
by UUID, not display name or alias. The model-only <span class="docutils literal">update_bundle</span> treats an
|
||||
<p>Retained declaration UUIDs remain unchanged unless explicitly edited. Source
|
||||
ownership remains unchanged except for the nested relocation rules above.
|
||||
Records match by UUID, not display name or alias. The model-only <span class="docutils literal">update_bundle</span> treats an
|
||||
explicit UUID replacement as removal and creation. The desktop instead uses
|
||||
<span class="docutils literal">update_bundle_with_identity_map</span> with the surviving-record provenance from
|
||||
<span class="docutils literal">prepare_command</span>: explicit identity edits and sequential identity swaps MUST
|
||||
preserve each original declaration's owning file and attached source trivia.
|
||||
preserve each original declaration's owning file and attached source trivia,
|
||||
unless the same edit changes its nested semantic parent and requires relocation.
|
||||
New records have no original identity mapping. The mapping MUST be injective,
|
||||
refer to existing records and preserve record classes. The
|
||||
result is recompiled, reindexed and checked for equality with the requested
|
||||
model before return. Source changes needed for undo are workspace history data;
|
||||
model before return. Equal model values do not imply a no-op if command
|
||||
provenance removes or replaces a declaration; the resulting model and source
|
||||
bundle together determine whether the accepted state changed.
|
||||
Source changes needed for undo are workspace history data;
|
||||
callers should retain complete compiled workspace snapshots rather than infer
|
||||
past omission/layout choices from a model-only undo.</p>
|
||||
<p>Canonical modular export is a distinct, explicit operation and does not promise
|
||||
original comment/layout preservation. The native editor integrates per-file
|
||||
drafts and source-project saves as specified below. The pure compiler performs
|
||||
no filesystem access. REST source endpoints currently exchange a canonical
|
||||
model; they do not yet exchange the multi-file source bundle.</p>
|
||||
no filesystem access. REST <span class="docutils literal"><span class="pre">source-project</span></span> endpoints exchange the accepted
|
||||
bundle with all its files. One revision and ETag protect the model and source
|
||||
together, including source-only comment edits; stored envelopes contain both.
|
||||
Canonical <span class="docutils literal">source</span> endpoints remain explicit model interchange. The precise
|
||||
transport, admission and persistence contract is in <span class="docutils literal">api.rst</span>.</p>
|
||||
<p><span class="docutils literal">parse</span> returns a complete structurally validated candidate or error
|
||||
diagnostics and no model. Engineering warnings alone MUST NOT reject a valid
|
||||
candidate. Parsing may stop at the first syntax/conversion error. Diagnostics
|
||||
|
||||
Reference in New Issue
Block a user