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

16 KiB

R3 semantic transition review — historical proposal

Status: the representation recommendation below is superseded by release-semantic-review-implementation.md. Root selected closed typed per-facet reviews (C) after the 11-case executable comparison and independent critique. Recursive SemanticValue/string paths and typed-carrier/mask storage are not the production choice. The engineering requirements below remain design context where consistent with that authoritative implementation contract. Production R3 is not yet accepted.

Current boundary and concrete outcome

transitions.rs already supplies direction, SelectedOnly/Required/Subtree scope, explicit source→target/kind mappings, preview changes/conflicts, per-field resolutions, a snapshot fingerprint and atomic commands. TraceBaseline stores only OA/SA TracedProperties { name, description }; TransitionField is only Name/Description and FieldChange contains string before/after values. trace_drift() therefore cannot detect a changed body, parent, reference or ordered contribution. Creation already maps typed bodies; updating an existing counterpart does not review those bodies. Retain the current pipeline and extend its review surface rather than replace it with a general graph synchronization framework.

An engineer must see a changed range, item list, parent, guard, occurrence, event trigger or interface contract; inspect its mapped proposal; accept selected changes or preserve intentional target differences; apply once, undo, and repeat without overwriting independent work. Neither direction is an automatic inverse. The original Capella iterative transition reference already linked in release-semantics-audit.md motivates conservative target comparison; the detailed decisions here are Studio's own contract.

Rejected historical representation proposal

Keep Name/Description unchanged for existing callers. Add a closed-catalog semantic field selector and typed values:

enum TransitionField { Name, Description, Semantic(SemanticPath) }
struct SemanticPath { segments: Vec<String> }
enum SemanticValue {
    Absent, Boolean(bool), Integer(i64), Unsigned(u64), Real(f64),
    Text(String), Enum(String),
    Reference(HistoricalRef),
    List(Vec<SemanticValue>),
    Record(BTreeMap<String, SemanticValue>),
}
struct HistoricalRef { uuid: Id, kind: SemanticRecordKind }
enum SemanticRecordKind { Element(Kind), Relation(RelationKind), Artifact }
struct ReviewedSemanticField { path: SemanticPath, value: SemanticValue }
struct ReviewedSemanticSide { fields: Vec<ReviewedSemanticField> }
struct SemanticBaseline { oa: ReviewedSemanticSide, sa: ReviewedSemanticSide }
// TraceBaseline retains existing oa/sa TracedProperties and adds:
// semantic: Option<SemanticBaseline>

enum FieldDecision {
    TakeSource, KeepTarget, Value(String), // Value remains prose only
    SemanticValue(SemanticValue),
}
// FieldChange.before/after become SemanticValue (Text for Name/Description).
// Add source_before/source_after and target_before so a reviewed delta is visible.

SemanticPath is not an arbitrary JSON Patch address. The core returns a finite catalog of valid paths for each supported record/variant and rejects unknown paths, duplicates, identity/header edits, invalid enum/value combinations and unsupported writes. The GUI displays human labels, not path strings. API/source uses the same catalog and typed value constructors. Represent missing optional values with Absent; distinguish it from an empty list or empty string. Finite numeric rules follow the actual R6 types.

Baseline field entries are sorted lexicographically by path and unique; the vector is intentional because JSON cannot use a structured SemanticPath as an object-map key. Internal lookup may use a BTreeMap. Types containing finite f64 values derive PartialEq rather than promising Rust Eq; validation rejects non-finite values before baseline storage.

Recommended path shape is parent, body/<field> and nested body/<constructor>/<field> as necessary; constructors are explicit read-only/switch proposals, never inferred from property names. Examples: body/DataField/minimum, body/StateTransition/guard, body/StateTransition/triggers, body/Occurrence/Exchange/reply_to. Use named stable identity selectors for collection-member proposals; never use a vector index as durable identity. Paths are arrays in JSON/source so no slash-escaping ambiguity exists. The exact path list is generated/tested from an exhaustive typed-model adapter after R6 freeze.

HistoricalRef values are baseline/proposal snapshots, not additional live graph edges. They retain deleted reference identities for detecting removals. They must not be pruned by ordinary target deletion or rejected as dangling live references. Reidentification explicitly remaps these historical UUIDs when it remaps an existing identity. Source prints uuid "..." inside a HistoricalRef; it cannot require an @alias for an object that no longer exists. Human preview labels resolve current names where available and mark removed references otherwise. This separation prevents deletion cleanup from erasing the evidence needed to show drift.

The release schema/API change to typed FieldChange is explicit; do not silently serve the new shape under documentation claiming old strings. Existing REST endpoint version can remain /api/v1 with an explicitly updated OpenAPI/model schema contract, but version-2 source/JSON migration only concerns stored models, not undocumented old transition-plan compatibility. Old retained client plans are stale and must be regenerated.

Snapshot coverage and drift

Every directly persistent semantic field of an element/relation is captured: name, description, parent, body kind/constructor and all typed body fields/references. Exclude identity, alias, layer identity, diagram placement/size/collapse and the trace baseline itself. Alias changes do not change engineering meaning. Type/layer changes are explicit counterpart mapping decisions, not an incidental editable field.

The implementation adapter must exhaustively cover:

  • Existing data/interface/port, constraint, state behavior text, availability/allocation, exchange endpoints/ports/items and mission/capability relationships.
  • R1 process/chain steps, control kinds, SequenceReference, SequenceLink guards/endpoints and contribution parentage.
  • R2 mission/include/extend/generalization/involvement fields, especially Extend condition and endpoint mappings.
  • R4 viewpoints, context/pre/postconditions, lifelines, message kind/timer expression/reply correlation/value annotations, scenario-use bindings, fragment/branch membership and guards.
  • R5 StateKind, Event Signal items/Time kind and expression/Change expression, transition kind and both typed trigger/effect lists, with descriptive strings separate.
  • R6 nature/mechanism, defaults/ranges/unit/direction/storage, supertypes, association roles/multiplicities/navigation, interface allocation protocols/order, and message value paths if accepted.

Owned-child membership and semantic incident-relation membership are also reviewed facets. Keep relation records separately identified; a parent→child relation is not a second opaque nested copy of an entire graph. Created/selected dependency records each get their own trace baseline. The parent review rolls up drift from owned descendants' traces and flags newly untraced descendants as unreviewed. Direct drift and inherited/related impact must be visually distinguished; a referenced shared type changing should show as a linked impact, not pretend the user's own field reference UUID changed.

For efficient storage, store each traced record's own fields once per side and direct ownership/relationship identity membership; do not embed the whole subtree into every ancestor baseline. A descendant removed from the live graph remains detectable from historical membership even when its child trace is removed. Aggregate drift walks current and recorded ownership with visited IDs and bounded work. Artifact links owned by the compared semantic record are reviewed separately (URI/revision/fragment/relation and membership); artifact content itself is not fetched or diffed. A trace's own rationale/artifacts are not recursively included in its baseline.

For a reviewed field, compare current OA with recorded OA and current SA with recorded SA, preserving intentionally unequal reviewed values. Drift is Unchanged/OaChanged/SaChanged/BothChanged as today. Any supported field lacking a semantic baseline is Unreviewed, never falsely Unchanged. Source change affecting only an unselected field remains drift after applying a Name-only review.

Mapping, three-way proposals and target preservation

  1. Freeze an explicit mapping table before generating proposals. Reuse a unique compatible trace or the engineer's exact mapping. Missing or multiple eligible mappings create actionable conflicts; no display-name matching. Resolve all references through the same table used by current creation. Mapping and source-selection changes invalidate all affected decisions, even if a string value happens to match.
  2. Compare mapped source-before/source-now with target-before/target-now. A scalar source change can apply when the target still matches its recorded value. If both sides changed the same scalar incompatibly, or a baseline is absent, require TakeSource/KeepTarget/typed merged value. If target-now already equals mapped source-now, it is a no-op that may be acknowledged. Preserve independent fields automatically.
  3. TakeSource for reference collections means apply the displayed source delta, not replace the entire target collection. Target-only members survive. Additions map by UUID and are shown individually. Removing a mapped source member proposes unlinking it; deleting the target semantic object is a separate explicit action with ordinary impact preview. Never cascade deletion across layers.
  4. Ordered membership is a distinct proposal from presence. When unchanged target ordering maps directly to the source baseline, source reorder can apply. If target-only contributions or independent reorder make insertion anchors ambiguous, require an explicit final target ordering; do not guess placements or discard extra steps. New mapped members may insert after a uniquely mapped unchanged predecessor, with source order preserved. A proposed order must include every surviving target member exactly once.
  5. A parent change is an explicit reparent proposal, with mapped destination and the actual affected target subtree visible. Changing a relation endpoint is an explicit endpoint proposal, validated against mapped kinds and allocation/scenario context. An unsupported layer-specific source relation has no destructive fallback. Keep target and explain the missing mapping.
  6. Constructor changes (e.g. Asynchronous→Timer, Time→Change, Record→primitive) are grouped typed proposals with all added/removed fields and affected owned records visible. The whole group must be compatible with retained target-only work. If it would invalidate independent children, references or scenario observations, block until explicit resolutions exist; do not hide it behind one generic Body checkbox.
  7. Many source records mapped to one target are allowed only when their resolved deltas agree for every touched facet. Incompatible merges/splits require explicit individual mappings/resolutions; no automatic reverse merge. Reuse remains idempotent and aliases/target IDs remain stable.

All retained independent work is still validated in the final candidate. Keeping an incompatible target field can make a chosen source proposal impossible; report that exact interaction rather than force either side to win silently. Creation of required dependency records remains supported, but the preview must name every creation and make existing mapped records' updates opt-in.

Baseline acknowledgement and command safety

KeepTarget acknowledges the current source and the intentionally retained target only for the selected field/facet. It does not copy the source or refresh unrelated baseline fields. TakeSource acknowledges the source and successfully resulting target. A manually merged value records that pair. No-op acknowledgement can update a baseline without emitting an EditElement. Cancellation changes neither model nor baseline.

Applying a plan still checks the exact project fingerprint and HTTP ETag/base revision, builds one candidate and runs whole-model structural validation before one atomic history/disk commit. An invalid candidate leaves the project, baseline, revision and undo history unchanged. Baseline updates occur in that same transaction after successful resolved model changes. Keep the current documented stateless server plan contract: a client plan is a candidate, not a cryptographically trusted receipt. Do not bypass ordinary typed command validation because it came from a preview.

Identity/reference remapping, stale-plan rejection, duplicate resolutions, wrong field types, unsupported paths and removal impacts need focused tests. Fingerprints remain change detection, not authorization or an execution capability.

Migration and integration sequence

V2 baselines retain exact name/description values and gain semantic: None. The first semantic review is explicitly Unreviewed and requires acceptance for differing typed values; do not fabricate historical body snapshots from today's model. Name/Description-only legacy behavior remains available without pretending semantic review happened. Golden V2 UUIDs/aliases/prose/artifacts remain unchanged. Historical reference values have explicit typed source constructors and OpenAPI definitions rather than escaped JSON blobs.

Implement after R6 shapes freeze: (1) exhaustive semantic snapshots and honest drift; (2) mapped scalar/parent/endpoint proposals with per-field decisions; (3) identified collection/ownership changes and order review; (4) native reviewer UX plus API/source/schema parity. Each milestone must have observable review behavior. These are one existing transition subsystem, not separate synchronization services.

GUI: show source and target labels, origin/current values and mapped proposal; group Properties, Ownership, Relationships, Ordered behavior and Artifacts. Include deliberate Keep target differences and unreviewed semantics. Selecting a reference opens its semantic record or mapping picker. Checkboxes control individual coherent proposals. A pending conflict identifies the field and affected target object. Do not show opaque UUID-only bodies, mark unchanged rows as changed, or advertise two-way automatic sync.

Required proof before accepting R3

  • Initial OA→SA transition of a complete process/state/scenario/data slice, then change datatype range/unit/default, field multiplicity, exchange items/ports, parent, guard, topology order, scenario binding and event triggers. Each produces exact drift/proposals; do the same in reverse.
  • Intentionally retain a target guard and add a target-only branch, field and interface allocation. Apply unrelated source changes and verify these survive byte-for-byte. Remove source contributions and verify target records are retained until explicitly deleted.
  • Exercise ambiguous endpoint mapping, constructor switch with dependent target children, simultaneous reorder, differing many-to-one proposals and absent V2 semantic baselines. No partial application or invented inverse.
  • Apply the same accepted review twice (second has no further semantic changes), undo/redo exactly, edit after preview then reject the stale plan, and fail one resolved field validation without advancing any baseline.
  • Prove exhaustive coverage by walking each actual supported typed payload and mutating one meaningful field/reference at a time against a reviewed baseline, including every R1/R4/R5/R6 property. The expectation is observed drift plus one justified proposal or explicit unsupported conflict, not just serialization equality.
  • Verify JSON/source stored-baseline roundtrip with deleted historical references, reidentification, real HTTP preview/apply with ETag races, disk restart, and native inspect at compact and normal widths.

Lead choices to freeze: typed SemanticValue/closed path catalog; field-specific historical snapshots rather than whole-subtree copies; explicit collection deltas preserving target-only members; old semantic baselines remain Unreviewed; no source deletion automatically deletes a target record. R6 names must be reconciled with its final accepted contract before generating the exhaustive catalog.