Files
ARCADIA-Studio/harness/release-data-contract.md

26 KiB

R6 data and interface contract proposal

Implementation freeze (2026-09-06): release-data-implementation.md supersedes the earlier version-number and generic-port proposals below. Current contract is schema/source4 with explicit1/2/3 migration; existing typed PortContract remains authoritative. Value Record default [] is allowed at minimum0; Reference fields have no explicit default. See docs/language.rst4.5 for the implemented specification.

Status: read-only design, 2026-09-05. No implementation, test pass, or native Studio acceptance is claimed here. This is the recommended integration contract for the lead to freeze before the R6 worker changes exhaustive enums. It extends the current typed model and transaction path; it does not introduce an instance database, expression evaluator, units engine, or plugin metamodel.

Evidence and scope

Current model.rs has Information/Material/Energy/Event in one item-kind enum, primitive/enum/Record type definitions, fields with type/multiplicity/unit, and Interface.items: Vec<Id>. references.rs centrally maintains these links. The desktop uses the generic typed forms, including an item picker; its kind selector still mixes Event with physical nature. The R6 gap and release proof are recorded in release-semantics-audit.md.

The native benchmark in release-capella-workflow-benchmark.md actually created an Operation exchange item, an interface, and an allocation of that existing item. The allocation is independently selectable, has its own transmission/acquisition protocols, and appears as a nested row while the original item retains its identity. The dictionary displayed nested class properties, enum literals, association role names and multiplicities. Numeric/default edits and category operations were not exercised; do not report them as native observations.

Primary-source support: Capella Information defines independent exchange mechanisms, operation-parameter directions, typed numeric/string bounds/defaults, and structured types/associations. This supports the feature families, not every simplification chosen below. Information.ecore. Capella's interface allocation is a relationship with an allocated item and sender/receiver protocol properties; its container is the allocating interface. CompositeStructure.ecore. Functional input/output ports are distinguished in the Arcadia reference, as already cited in the audit. Arcadia metamodel.

The detailed rules below are explicit Studio design decisions. They are not claims of complete Capella/UML compatibility.

Proposed persisted shapes

Retain existing headers, layer ownership, field IDs, relation IDs and TypeRef. All additions belong to the final schema/source version 3. Reference-bearing properties participate in the existing reference visitor, delete impact, transition closure and semantic review snapshot.

enum ExchangeItemKind { Information, Material, Energy } // remove legacy Event via migration
enum ExchangeMechanism { Unspecified, Flow, Event, Operation, SharedData }
enum ParameterDirection { Unspecified, In, Out, InOut, Return }
enum FieldStorage { Value, Reference }
enum DataValue { Boolean(bool), Integer(i64), Real(f64), String(String), Enumeration(String) }

enum DataTypeDefinition {
    Boolean,
    Integer { minimum: Option<i64>, maximum: Option<i64> },
    Real { minimum: Option<f64>, maximum: Option<f64> },
    String { minimum_length: u32, maximum_length: Option<u32> },
    Enumeration { literals: Vec<String> },
    Record { supertype: Option<Id> },
}

SharedBody::ExchangeItem {
    kind: ExchangeItemKind,
    mechanism: ExchangeMechanism,
    supertype: Option<Id>,
}
SharedBody::DataType {
    definition: DataTypeDefinition,
    default: Option<DataValue>,
    unit: String,
}
SharedBody::DataField {
    type_ref: TypeRef,
    minimum: u32,
    maximum: Option<u32>,
    unit: String,
    direction: ParameterDirection,
    storage: FieldStorage,
    default: Option<Vec<DataValue>>,
}
SharedBody::Interface { supertype: Option<Id> }

enum TransmissionProtocol { Unspecified, Synchronous, Asynchronous, Unicast, Multicast, Broadcast }
enum AcquisitionProtocol { Unspecified, Read, Accept }
RelationBody::InterfaceItemAllocation {
    source: Id, // Interface
    target: Id, // ExchangeItem
    order: u32, // presentation order within the source interface
    transmission: TransmissionProtocol,
    acquisition: AcquisitionProtocol,
}
RelationBody::DataAssociation {
    source: Id, // Record DataType
    target: Id, // Record DataType
    source_role: String,
    target_role: String,
    source_minimum: u32,
    source_maximum: Option<u32>,
    target_minimum: u32,
    target_maximum: Option<u32>,
    source_navigable: bool,
    target_navigable: bool,
}

InterfaceItemAllocation is deliberately distinct from the existing ExchangeAllocation (boundary exchange realizes a work exchange). The former is a normal relation record, so it already supports artifact traceability, selection, query, identity changes and undo. Interface.items must not remain a second writable list of the same fact. Use a derived interface_items()/effective_interface_allocations() helper wherever existing code needs the items.

New GUI defaults: Information + Unspecified mechanism; bounds absent; String length 0..*; type default absent; field 1..1, Unspecified direction, Value storage, no field default; no supertype; protocols Unspecified. Append an allocation after existing order values; ties are valid and sort by alias/UUID, so ordinary relation creation with order 0 remains deterministic. The contextual “Operation parameter” action sets In and the “Physical flow” action sets Material + Flow. Defaulting generic creation to an asserted mechanism would invent an engineering decision.

Item mechanism, parameter and message semantics

  • Nature describes what crosses the boundary; mechanism describes how its contract is used. Material/Energy + Flow is directly useful. Do not derive nature from fields or rename an Event item to Material. Material/Energy with Event/Operation/SharedData warrants an explanatory warning (often the author intended information about the physical exchange), not an unsupported categorical rejection.
  • Unspecified mechanism is a valid draft with a located completeness warning. Flow is an ongoing value/physical transfer contract; Event is a notification; Operation is an invocation with input/output parameters; SharedData describes access to shared information. No mechanism schedules or executes behavior.
  • Parameter direction is interpreted from the operation provider/callee's perspective: In travels on invocation, Out and Return on response, InOut participates in both. Only fields directly owned by an Operation item may set a direction other than Unspecified. Record fields describe structure and have Unspecified direction; they inherit their containing parameter's direction when viewed in a message.
  • An Operation may have no parameters or no response parameters (a void operation). Allow multiple Out fields and at most one Return field across the effective inherited parameter set. An Unspecified operation parameter is a warning; it must not silently become In. In/Out/InOut/Return on non-operation content is a structural error; changing mechanism therefore requires resolving incompatible directions in the same transaction.
  • Keep R4 call/reply pairing and endpoint/path checks. Add phase-aware value annotation checking only where the message actually carries an Operation item: Call values can address In/InOut; Reply values Out/InOut/Return. Unspecified parameters warn and remain addressable during authoring. Event/Flow exchange annotations do not gain operation directions. A Call without an Operation item is a warning, preserving existing generic call modeling; it is not reinterpreted or broken on migration.

Necessary R4 reference seam

MessageValue { field, expression } currently identifies a field definition. When origin and destination both use the same Coordinate record, both have the same nested latitude field UUID. One UUID cannot disambiguate two argument values or enforce their possibly different directions.

Recommend adding MessageValue.path: Vec<Id> (default []) with the existing field as terminal ID. path contains the enclosing DataField IDs from the direct item parameter down to, but excluding, the terminal field. Direct parameters have an empty path. Each hop must follow Value storage through a named Record's effective fields. Uniqueness is (path, field) rather than field. Aliases render each hop in text, and the picker displays origin.latitude / destination.latitude with both UUIDs maintained.

For old empty-path nested annotations, resolve exactly one valid path when possible during version-2 migration. If several paths exist, retain the old field-definition annotation and issue message_value_ambiguous_path; do not guess an argument or discard the expression. The editor offers explicit path selection. This unresolved legacy form has no asserted parameter direction, so only warn until resolved. New graphical annotation creation always writes a concrete path. This is a small R4 shape amendment to freeze with R6, not a second scenario engine.

Types, values, multiplicity and units

Numeric ranges are inclusive; an absent bound means unbounded within the represented Rust numeric domain. Integer literals are signed 64-bit; Real literals are finite IEEE-754 binary64. Reject NaN/infinities, integer overflow, reversed ranges and defaults outside range. Do not compare integer ranges by converting to f64. The spec must explicitly avoid claiming arbitrary-precision decimal arithmetic. Canonical serialization retains a valid JSON/source numeric spelling with round-trip value equality.

String lengths count Unicode scalar values (chars().count()), not UTF-8 bytes or grapheme clusters. Enforce nonnegative ordered bounds and default lengths. Enum literals remain case-sensitive unique nonempty strings; a default must match a literal. Rename/remove of a literal is a transaction affecting defaults or is rejected if it would leave invalid values. No heuristic matching by display name.

DataType.default is a scalar value for Boolean/Integer/Real/String/Enumeration definitions. DataField.default = None means no explicit field default, and Some([]) is an explicit empty collection allowed only at minimum 0. A nonempty field default is a sequence of typed scalar values with length in the field's multiplicity. A scalar field therefore uses a one-value vector. Type defaults supply the value of an individual element; they do not instantiate a required number of values or make a collection present. Field defaults override type defaults; a derived/read-only inspector row can show the effective element default. No numeric/string coercion; Real(1.0) is distinct from Integer(1).

Record/object-reference literals and executable default expressions are outside this concrete scalar-default feature. Reject them explicitly instead of accepting opaque JSON or validating only part of a literal. A record may still be described completely by its typed fields and their scalar defaults. R4 MessageValue.expression remains a descriptive expression annotation, not a DataValue claimed to have been evaluated.

Unit text is an engineering annotation, not a dimension or conversion registry. Numeric named types can define a reusable unit. An empty field unit inherits the type unit; an explicit field unit equal to that unit is redundant but valid. A different nonempty unit on a named type with a nonempty unit is a warning and must be shown clearly, because the tool cannot prove conversion compatibility. Units on nonnumeric fields are preserved with a warning (legacy models permit them). Never convert bounds/defaults based on unit strings. Required physical acceptance can use kg/s, V, A and degC without introducing a units framework.

Records, specialization, associations and ownership

Record specializations have at most one same-layer Record supertype. Effective fields are inherited fields followed by locally owned fields. Preserve inherited field UUIDs; show their origin/read-only ownership in nested rows. Reject cycles and local fields with a name already present in the inherited set; this release has no implicit shadowing/redefinition. Supertype removal can remove inherited fields from the effective payload and must trigger the existing annotation-pruning/impact path. Inheritance is not model containment: deleting a derived type never deletes its base or base fields.

ExchangeItem and Interface each get the same single-supertype pattern, constrained to their own family and layer, acyclic. A specialized item inherits fields and must preserve the base nature and any specified base mechanism; it may refine an Unspecified base mechanism. Incompatible changes are rejected as an explicit contract conflict. An interface inherits allocations; a local allocation of an already inherited item is rejected rather than creating ambiguous protocol overrides. Editing the original allocation changes all inheritors, with reverse usage navigation. Empty records/items/interfaces remain valid authoring drafts, with warnings where an allocated/carrying contract lacks definition.

Value storage means the field's values form part of the serialized/transported record value; Reference storage means a link to a separately identified record object. Reference storage requires TypeRef::Named pointing to a Record, permits no scalar default, and does not imply ownership/deletion of the target type. Model containment is always the DataField's actual parent; changing storage never reparents the referenced type.

Detect cycles in the graph of mandatory Value record fields (edges with minimum > 0), including effective inherited fields, and warn record_no_finite_value: such a type cannot have a finite fully populated value. This is a completeness warning rather than rejection because existing V2 record references lacked storage semantics and the model describes types rather than asserting an instantiated value. Optional (minimum 0) or Reference cycles are permitted without that warning. This distinguishes a linked-list/tree definition from an impossible required self-value while keeping saved models readable. Traversal must use a visited/path guard and bounded work; do not reject every recursive type just to simplify recursion.

DataAssociation is a binary reference relationship between Record types. It does not add contained fields, carry a payload transitively, or imply deletion of instance objects. Self-associations are valid. The source-end multiplicity says how many source instances may be related to one target instance, and vice versa; a multiplicity is not a count of model elements. Role names belong to the named ends. Navigation toward an end is permitted when that end's navigable flag is true. Empty roles are a completeness warning, invalid ranges are errors, and same-type self-associations require distinct nonempty role names when both ends are navigable. Associations remain distinct identified relations even when endpoint types match; duplicate role contracts warrant a warning rather than global source/target deduplication.

Do not add association composition/shared aggregation in this tranche without their ownership semantics: Value fields already supply nested value composition, and binary association supplies cross references. Exchange categories and persistent diagram filters remain a separate named release requirement; they must not be encoded as fake DataAssociations or fake exchanges.

Interface allocation and port rules

The allocation source is an Interface and target a same-layer ExchangeItem; at most one direct allocation per source/target pair. Sender/receiver protocols belong to this relationship, not the global item. A nested interface row selects the allocation UUID, opens allocation protocols, and offers “Open item” / “Find usages”. Removing the row removes allocation only. The item and other interfaces' allocations remain. Protocol enums represent the choices actually benchmarked; no constraint is invented between protocol and item mechanism without evidence. An Unspecified protocol is valid and need not generate noise.

Provided/required interface relations retain their current meaning. Boundary/work exchange compatibility must use the effective inherited interface-item set, with the same-item or documented item-specialization acceptance rule. Recommendation: an exchange may carry a specialized item when the interface allocates its base (subtype satisfies base contract); the reverse does not satisfy a specialized contract. Compare UUID plus an explicit ancestry walk, never names. Reuse this rule in exchange allocation and port checks.

Functional ports on Activities/Functions are directed In or Out. Participant ports retain In/Out/InOut. Preserve legacy function InOut as an actionable warning so saved projects remain readable. New UI creation and Change direction controls expose only In/Out for work owners. For consistent REST/source draft behavior, core validation continues to report a warning for an explicit legacy/author-entered InOut; do not create an unpersisted origin flag or make input channel determine validity. Publish this draft allowance honestly in the spec.

Provide a transaction-level Split bidirectional work port action. Keep the original port ID as the selected primary direction, create a sibling opposite port, preserve owner/interface, remap source_port usages to Out and target_port usages to In, and preview ambiguous non-exchange references/artifact attachments rather than duplicating their meaning silently. The action must be undoable; input/output placements are ordinary port placements. An alternative strict ban on all new API InOut would require a persisted explicit legacy distinction or automatic load-time split, neither recommended for this minimal contract.

Source, API, migration and lifecycle

  1. Freeze final schema_version = 3 and arcadia 3 after R5/R6 shapes are agreed. Retain explicit readers for original schema/source 2 and V1. The two golden V2 fixtures must stay byte-for-byte unchanged. Treat existing staged version-2 R1/R2/R4 additions as a documented compatibility input, not as evidence that original V2 carried their semantics.
  2. V2 Information/Material/Energy becomes the same nature with Unspecified mechanism, never guessed Flow. V2 Event becomes Information + Event. Old Integer/Real/String/Record constructors receive empty bounds/default/supertype; old fields retain UUID, parent, type, multiplicity and unit, plus Unspecified direction / Value storage / no default. Old interfaces gain no supertype.
  3. Convert each old Interface.items entry into one InterfaceItemAllocation, with deterministic UUID v5 using the project UUID namespace and stable name v2-interface-item:<interface-uuid>:<item-uuid>. Generate a deterministic collision-free alias from the endpoint aliases/IDs, protocols Unspecified, and order equal to its original zero-based list index. Validate collision against all existing object IDs and aliases; fail a pathological collision with a located migration diagnostic rather than overwrite anything. This preserves the existing list's visible ordering without retaining two writable representations of membership.
  4. Keep all old element/relation/diagram/node/trace/artifact identities. Migrating a file does not change stored revision until explicit save/import commit. Canonical output is version 3, so opening old source may show an explicit upgrade diff. The migration report lists semantic defaults and generated allocation IDs. Update V1 migration through the same V2-to-V3 semantic normalization rather than two divergent new-data migrations.
  5. Text uses the existing readable constructor grammar, typed numbers/enum constructors, and unquoted aliases for references. Add supertype and MessageValue path to reference-aware fields; direction, storage, mechanism, protocols and typed DataValue constructors to the catalog. OpenAPI describes each exact closed object/enum, finite ranges, reference semantics and migration defaults. No JSON blob escape hatch. Keep the current REST query/transaction/candidate/ETag endpoints; new relation kinds are normal queryable records, not new services.
  6. Shared helpers should compute effective fields, item ancestry, effective interface allocations and carried field paths once per validation snapshot. The public carried_fields() helper can derive the terminal-ID set for existing callers. Direct fields of either storage kind are carried, including inherited fields; recursive descent only follows Value record fields and never association/Reference targets. Make validation, picker compatibility, API diagnostics and transitions call these helpers to avoid divergent definitions.
  7. Reidentify/remap visits every supertype, association/allocation endpoint and every MessageValue path entry. Required transition closure copies/remaps required types, supertypes, fields and interface allocation records; associations between included types are preserved, but do not cause a whole dictionary to be copied merely because a type has a reference association. Exact dependency policy is visible in the existing preview. R3 baseline/diff must include bounds, default values, units, direction, storage, protocols, specialization and end-role contracts.
  8. Delete impact never removes a base type merely because its user is deleted. Deleting a base with surviving derived contracts or a named type with dependent fields must show those consequences and use the existing explicit cascading transaction policy; do not silently substitute String. Removing an interface allocation does not delete its item. Removing carried-field connectivity prunes or explicitly reports affected R4 annotations according to the current deletion policy; no dangling field paths survive a successful transaction.

Observable release proof and focused regression plan

  • Build OA Material fuel Flow and Energy electrical Flow with field quantities using reusable bounded types (MassFlow, Voltage), and an information Operation with In/InOut/Out/Return parameters using an enum and a nested record. Create these through contextual GUI actions, edit a field row, and read the resulting contracts without raw constructors.
  • Allocate the same operation to two interfaces; show nested allocation rows and separate global item. Change one allocation's protocol and verify the other remains unchanged. Select a row, follow item/owner/reverse usages, attach a development artifact to the allocation, delete one allocation, undo, and reload.
  • Define a base record plus specialized record with an inherited enum field, a distinct local bounded value, and an association to another record. Verify nested inherited rows, role labels/multiplicities, navigation and no noisy allocation arrows. Use a reused Coordinate record twice in operation parameters and verify scenario value pickers show both concrete paths.
  • Exercise a Call with input values and paired Reply with output/return values. Rename/reidentify the record field and operation; annotations remain attached. Wrong-direction values, invalid field paths, wrong enum defaults, integer overflow, non-finite reals, reversed limits, invalid multiplicity and inheritance cycles produce located diagnostics and preserve the last accepted model.
  • Verify empty types/interfaces, Unspecified mechanisms/directions, recursive optional/reference data and legacy InOut produce understandable drafts/warnings, without crashes or recursive traversal stalls. Run the work-port split and verify both directions, all exchange references, undo and saved source.
  • Round-trip the same model through GUI save/load, canonical source parse/format, JSON, REST candidate/transaction/export/restart, and OA-to-SA/back transition preview. Query allocation/association records, reidentify their endpoints, and verify artifact links plus R3 semantic drift. Confirm invalid REST changes remain atomic with unchanged ETag/disk state.
  • Keep original V2 golden migrations and add a small fixture containing legacy Event, InOut functional port, Interface.items, nested records, defaults absent and reused nested message field ambiguity. Verify deterministic new IDs, no lost old IDs/content, precise migration reports and canonical second-save stability.
  • Native inspect at normal and compact widths: nested rows, long qualified field names, inherited origin, bounds/unit labels, protocol editor, source lint navigation and diagram association ends. Numeric entry must accept temporary editing text without committing invalid data or moving keyboard focus unexpectedly.

Decisions the lead should explicitly accept or alter before coding

Recommended: separate nature/mechanism with honest Unspecified defaults; scalar/list-of-scalar typed defaults with finite numeric domains; one same-family supertype; reference associations and Value/Reference field storage; allocation as an identified relation; MessageValue path qualification; legacy InOut as a visible draft warning with an explicit split action. Each choice has a specified migration above.

The main bounded limitations are arbitrary-precision decimals, record/object-instance default literals, expression evaluation, dimensional conversion, association aggregation/redefinition and multiple inheritance. These must be documented as unsupported, not silently represented as unchecked text or described as completed Capella semantics. Their absence does not prevent the listed physical, quantity, operation, record, interface and traceability workflows. Exchange categories/filter work remains required elsewhere in the release plan.