Preserve canvas correction failures and bound scenario evaluators

This commit is contained in:
2026-09-12 14:59:08 -07:00
parent e2c027e54c
commit 8112dea7a3
38 changed files with 10393 additions and 1 deletions
@@ -0,0 +1,79 @@
# Issue 21: nested call activation design and evidence
Advisory contribution by `/root/nested_call_design`, 2026-09-12. Root owns issue 21 and integration; baseline supplied by root: `f7d84fa`, `codex/behavior-authoring-20260910`. Read current root sources without editing them. No native input, compilation, tests, Git changes, remote coordination, or production implementation performed. Proposed checks below are not passes. The user-owned PROJECT_GOAL remains untouched.
## Finding
The reported four-message model `Call1 L1→L2, Call2 L1→L2, Reply2 L2→L1 reply_to Call2, Reply1 L2→L1 reply_to Call1` is already expressible. The core does not require stack-order returns. `docs/language.rst:675` and `oa-core/src/validation.rs:1672` require exact call identity, guaranteed-before order, reversed endpoints and no multiply executable duplicate reply. A rendering fix must not impose a new LIFO constraint or change correlations.
The direct defect is scene projection and paint geometry:
- `oa-desktop/src/behavior.rs:1295–1322` collects calls from the row BTreeMap, hence UUID order, and invokes `activation_sequence` with each receiver's identical lane x. It has no concept of overlap/depth.
- `activation_segment` at 1601 creates all bars at `x-4`, width 8. Every overlapping call has the exact same horizontal footprint. Closed calls also append a width-14 cap at that same x. The last UUID-painted bar can obscure either the inner or outer call. Sorting alone cannot make two same-x bars readable.
- `occurrences` at 1463 routes messages to lane centers, including 24-pixel self-message drops. `activation_sequence` starts a self-call at its send row y instead of its actual receive point y+24. A self-reply ends an activation at send row y; that is appropriate for the callee, whose execution ends when the reply is sent.
- `draw` at 2060 paints activation boxes with the generic rounded-card path, then lifeline guide strokes and messages over all boxes. A lifeline therefore runs through a filled execution bar. Moving bars without defining draw roles/layers leaves that defect. `hit` shares generic BoxItem records; thinner exposed nested shoulders need hit tests against actual visible bar geometry.
The user's `.local/gitea-issue21.png` shows two leftward calls and an incomplete-looking second execution (ellipsis). It does not expose exact reply_to values or the final reply. Do not diagnose wrong correlation from this crop, nor assume every visible Reply belongs to the most recently drawn Call. Inspect the saved model when reproducing it.
## Comparable primary evidence
The existing local `harness/release-capella-scenario-benchmark-20260910.md` established Activity/lifeline creation and a dragged LOOP only. It did **not** exercise nested calls. It cannot support a claim that nested activations have now been reproduced in installed Capella.
The official [Sirius sequence user guide](https://eclipse.dev/sirius/doc/user/sequences/Sequence%20Diagrams.html#Executions) depicts recursive execution rectangles on lifelines and on executions, and its complex-message example includes a self-call nested inside an active receiver. This supports offset execution rectangles and meaningful send/receive anchors as the comparable visual pattern. The [specifier guide](https://help.eclipse.org/latest/topic/org.eclipse.sirius.doc/doc/specifier/sequences/Sequence%20Diagrams.html) configures execution start/end finders and recursively reused execution mappings. These are source/documentation observations, not a new native Capella test.
[Capella Interaction.ecore at 5537297](https://github.com/eclipse-capella/capella/blob/5537297b071fff809285bc63989f5067d675bc52/m2/plugins/org.polarsys.capella.core.data.def/model/Interaction.ecore#L589) represents Execution as a TimeLapse covering an InstanceRole, with start and finish concepts. Studio currently derives call bars from occurrences and has no standalone execution or thread record. Copying Capella's model wholesale is unnecessary for this bounded defect; offset placement must remain a derived display, not a new ownership or execution-stack assertion.
## Recommended bounded contract
Call identity comes only from the Call occurrence UUID. Its responding messages come only from Reply.reply_to. Labels, exchange identity and row adjacency never establish correlation.
Keep the existing branch-aware correlation traversal as the semantic starting point. First produce typed activation segments containing call ID, receiver lifeline, segment start/end, enclosing branch path and termination kind (reply / branch boundary / unresolved end). Then assign horizontal placement, then route final message endpoints, then draw/hit against those final shapes. Avoid deriving semantics back from screen rectangles. Keep raw row order separate from final send/receive endpoint y.
Prefer a dedicated `ActivationSegment`/`ActivationLayout` over anonymous title-empty `BoxItem` conventions. Expose the same resulting rectangles and call/reply anchors to painting and hit testing. A local module `behavior/scenario_activation.rs` is a bounded home; this needs no persisted schema change.
Use a fixed receiver-relative rightward offset for nested bars regardless of the direction from which a call arrives. A proposed starting style is width 12 and offset 6 world pixels, square corners, with outer bars painted before inner bars. These dimensions are a design candidate requiring minimum-scale visual QA, not an assertion that six pixels is already usable. Make corresponding closed cap edges consistent with the bar instead of the existing oversized generic capsule. At depth >0 there must be an exposed shoulder and separately visible start and end edges. Selection cannot be the only way to reveal an inner call.
Depth/columns are **visual overlap separation**, not inferred synchronous thread ownership. Assign deterministically in semantic event order (with UUID used only as an otherwise-unneeded final tie-breaker), independently per receiver lifeline and compatible branch path. Retain one column for a call through its segments; reuse a column after that call's relevant closed extent. For well-nested complete intervals, the inner call occupies the next column. Crossed complete intervals also require distinct columns, but their actual termination y values remain crossed. Never shorten or re-correlate one to make a pretty nesting tree.
Calls wholly in different ALT operands do not consume each other's depth. Calls wholly in different PAR operands are also not evidence of nesting: branch layout is presentation, not chronology. Inherited outer activations may have separate segments in each operand and need to be compared only against activations in that operand. Reuse the existing ALT/PAR/OPT/LOOP open-path outcomes; add a regression before refactoring them. If a pure rectangle-overlap allocator is used, verify it cannot mistake the vertically stacked operands for a single execution path or merge distinct returns.
### Exact examples and bounds
| Scenario | Expected display and semantic boundary |
| --- | --- |
| User sequence C1,C2,R2,R1 | L2 C1 starts at receive(C1), ends at send(R1); C2 starts at receive(C2), ends at send(R2), shifted one column. Both bars and both terminators remain visible and independently selectable. Two ordinary calls on L1 do not create an invented L1 execution. |
| Sequential C1,R1,C2,R2 | Both L2 bars may use base column. No gratuitous staircase from counting all preceding calls. |
| Crossed C1,C2,R1,R2 | Preserve exact correlation and both extents; overlapping bars use separate columns. This core-admitted model must not be rejected or changed into C1,C2,R2,R1. Do not call it proof of a single nested thread. |
| Self-call on inactive L1 | The routed loop leaves the lane and returns to the new bar after its vertical drop. Bar top equals final receiving endpoint y, not the upper send row. An inactive caller has no fabricated execution before receipt. |
| A→B C1; B→B C2; self R2; B→A R1 | C2 offsets from C1 on B. C2 call starts at the active parent edge and ends at child start; R2 starts at child end and returns to the still-active parent. Parent remains visible outside the child. |
| A→B C1; B→A C2; A→B R2; B→A R1 | Bars belong to B for C1 and A for C2. Depth is per lifeline. Merely having an outgoing Call does not establish an A parent execution. If A was already active from an earlier received call, C2 is offset on A. |
| Incoming calls to same lifeline from different senders | Use exact receiver and interval overlap, not exchange identity or sender equality. No thread relationship may be inferred. |
| ALT inherited outer call with Reply in one operand and no Reply in another | Preserve segmented outer bar, exact matched end in returned operand, explicit open mark in unresolved operand, and existing post-fragment continuation rule. No bar across branch headers or false unconditional return. |
| PAR sibling local calls | Render each in its own operand and correlate locally. Do not increase second sibling depth just because its row is later. Keep inherited outer-call behavior separately covered. |
| Missing Reply | Preserve an explicit incomplete/open indicator; do not fabricate a reply or a full known execution duration. Current isolated open-call stub is 26 pixels. A stub cannot be used as evidence that activity definitely ended there. If it overlaps another displayed stub, separate them. Unknown semantic duration is not proof of nesting at a later call. |
| Reply without reply_to | Remains a draft diagnostic and ordinary Reply line; it closes no bar. |
| Missing sender/receiver | Preserve editable draft fallback. No invented lane or anchor. A Call with known receiver but missing sender may retain a clearly incomplete receiver activation, following current draft behavior. |
Anchor rules: for ordinary inter-lifeline calls/replies, connect to the side of the relevant execution facing the other endpoint; a Reply's source execution is its exact correlated Call. Its target can attach to an unambiguously active caller execution; absent one, attach to the caller lane. When several independent active executions could be the caller, do not guess a semantic parent: use the lane or an explicitly documented neutral rule. For self-messages, route the loop outside the outermost involved bar and attach at parent/child edges; never route a loop through the bar. Compute label bounds from final route, not stale lane centers.
## Candidate comparison
1. **Paint the selected bar last / change color only:** small but fails visibility without selection and cannot distinguish same-x shapes. Reject.
2. **Count preceding Calls per lane:** small but staircases sequential pairs, depends on unresolved calls, and falsely nests branch siblings. Reject.
3. **Single global active stack:** apparently matches common examples but imposes unmodelled thread and LIFO assumptions, and mishandles branches/crossed correlations. Reject.
4. **Typed correlation segments plus deterministic path-aware overlap placement and shared anchors:** modest renderer refactor, preserves existing source/REST semantics, and admits concrete paint/hit evaluators. Recommended.
5. **New explicit execution/thread metamodel:** potentially useful later but not justified solely by this display defect; larger source/API/transition burden and more uncertainty before the reported scenario works.
## Required evaluator (not executed)
Start with actual core-built Project fixtures, validation and canonical source round trips, then pass those accepted Projects to the production Scene. Do not use a miniature fake project or a reimplementation of activation logic as the sole oracle. `.local/nested-call-fixtures-20260912.rs` supplies deterministic actual-core fixture construction for a first gate; root must compile/run it, inspect warnings and add the resulting paths to native verification. The probe intentionally uses missing exchange references to isolate activation semantics in both layers; native release acceptance should also repeat the user case with an actual bound exchange and values.
For C1,C2,R2,R1, inspect final production activation records: top/end match actual line receive/send vertices, width positive, child x differs, exposed horizontal interval of each is nonempty, and neither terminal is swallowed by another fill. Permute UUIDs while keeping steps/correlation identical; geometry and paint order must be equivalent. Inspect selected and unselected cases, both lane directions, self-call drop, crossed returns, 3+ nesting levels and two different receiver lanes.
Call `Scene::hit` on an exposed inner shoulder away from message lines and labels, and on an exposed outer shoulder. Expect the respective Call IDs at default/minimum scale and nonzero camera pan. Test each matching Reply line separately. Activation body clicks must not select the lane or unrelated outer Call. Account for actual `8/scale` line hit tolerances: mere bar-center checks near message rows can pass by hitting a line instead of the bar.
Paint order gate: backgrounds/fragments, lifeline guides, activation bars shallow→deep, message routes/arrowheads, labels/selection adorners as appropriate. Lifeline guides must not appear through filled activation interiors; messages must end at intended bar edges. Generic rounded-card rendering is inappropriate for narrow execution rectangles.
Use actual Iced draw output/native screenshot at default/minimum scale in 1100×700 and normal larger window. Verify exposed shoulders, starts/ends, self loops, line dashes, unselected contrast and selected outer/inner strokes. A Scene coordinate test does not prove paint layering. Exercise selecting each bar and each reply, editing reply correlation, changing order, Undo/Redo and save/reopen. Canonical source and the full accepted model must retain IDs/steps/endpoints/values; merely selecting/opening never dirties the model. Reuse existing valid lifecycle/header/ALT gates and rerun affected ones.
This bounded repair cannot satisfy issue 20/22's free arrangement/editing requirements or the overall Behavior UX objective. It does not deliver persisted lane placement, arbitrary visual spacing, process geometry, cross-operand movement or a general execution metamodel. Root should record which slice is integrated without closing broader work.
@@ -0,0 +1,17 @@
# Issue 21 source implementation handoff
Commit **43c2d51**, branch `codex/nested-call-956atf-20260912`, worktree `.local/nested-call-implementation-20260912`, base `f7d84fa`. Root owns issue 21 coordination, integration, compilation, native validation and release. This contribution changes only `behavior.rs`, `behavior/scenario_activation.rs` and `behavior/scenario_activation/tests.rs`.
Implemented typed call/receiver/scope activation segments, semantic-order overlap columns, exact-correlated Reply source anchors, self-call receive versus send y, and shared final bar/route geometry for hit testing. Square activation fills paint over lifeline guides, under cards/messages, shallow before deep. Large line hit tolerances follow actual activation-body hits so zoomed-out shoulder points do not select a nearby unrelated message. Selected and unselected bars share geometry. No schema, source semantics, endpoint IDs, reply correlation or persisted layout change.
Twelve production Scene regressions construct actual OA/SA Projects with validation and canonical source round trips. Coverage includes both directions; sequential/nested/crossed and permuted UUIDs; self/reentrant calls; three exposed nested columns; exact canvas press/release selection under pan and scales 1/.25/.05; incomplete correlation; ALT/PAR scope separation; inherited outer execution; and fragment-boundary false-nesting falsifiers. Existing direct-canvas ALT/open tests now inspect typed activation records.
Independent source critic `/root/nested_call_design/activation_source_review` found (1) scope-clipped crossed calls appearing nested and (2) ALT sibling return order incorrectly rejecting true nesting. Corrections retain correlation-level reply endpoints and compare only compatible reply paths; critic rechecked both. This is source review, not independent runtime acceptance. The critic disclosed one initial read-only git diff despite its requested no-Git boundary; no Git mutation occurred.
Executed: rustfmt with edition 2024 and skip_children=true on the three owned files; unstaged and staged `git diff --check`; owned-files commit. No compiler, Cargo, desktop/native input, Gitea or root tracked edits. Initial checkout failed on historical long filenames; per-command `git -c core.longpaths=true worktree add` succeeded without changing global settings or overwriting existing work.
Root had previously compiled/run `.local/nested-call-fixtures-20260912.rs` against exact full425 core dependencies: `.local/native-qa/nested-call-f7-core/build.json`, `fixtures.json`/log; output `.local/native-qa/nested-call-fixture-20260912`; 132 elements / 77 completeness warnings. This validates that separate fixture generator, not the new desktop implementation.
Next: root compiles commit 43c2d51 and runs `behavior::scenario_activation::tests`, existing `behavior::direct_canvas_tests` activation/ALT/selection gates and affected workspace/input tests. Verify actual unselected and selected paint, both-direction message edge anchors, loops, shoulder selection, source correlation edits, Undo/Redo and save/reopen with a bound exchange. The fixture intentionally omits exchange references to isolate activation geometry; repeat the user model with actual exchanges/values for native acceptance.
Width 12 and offset 6 are world-pixel style candidates. At scale .05 they become .6/.3 screen pixels: pointer identity tests at that scale do **not** prove raster legibility or practical selection. Root explicitly retains native evaluation at realistic and minimum scale. Incomplete isolated calls keep the existing 26-unit open stub; branch continuations remain explicit open continuations. No general execution/thread metamodel or new LIFO rule is introduced. This work does not satisfy issues 20/22's free geometry or the broader Behavior UX objective.
@@ -0,0 +1,58 @@
# Issue 25 — remove misleading canvas activation controls
Root owns Gitea issue 25 (claim/recheck comment 668), compilation and native
acceptance. This worker owns the isolated worktree
`C:/Users/User/Documents/Git/ARCADIA-Studio/.local/always-active-canvas-20260912`,
branch `codex/always-active-canvas-956atf-20260912`, baseline `e57c390`.
## Source finding and scope decision
There is no canvas activation gate to turn on. The production Viewport accepts
the first clean pointer press, passes semantic hits/tools to its child, and
focuses that canvas through ordinary pointer ownership. Gesture eligibility is
derived from valid clean ownership, not either removed control.
The toolbar's `Canvas` control only dispatched keyboard Focus. The header's
`Focus canvas`/`Restore panels` control toggled presentation panel visibility;
top-level views.rs reads the corresponding presentation state. Neither enabled
editing. F6/Tab focus operations and pointer focus are legitimate separate input
ownership policies, particularly while Inspector or a form owns typing.
Parent explicitly approved removing both affordances and the unused controls()
focus argument while preserving the independent existing panel-layout command
semantics. Details/panel redesign remains issue 24. Free geometry remains issues
20/22. No behavior.rs or scenario_activation files changed.
## Changes
- Remove `Canvas` and `Focus canvas`/`Restore panels` widgets from process and
scenario presentation. Remove their local event/intent plumbing and the shared
controls() focus argument.
- Keep F6/Tab/Enter/Space/Escape guidance on the remaining toolbar tooltip.
- Extend actual mounted process OA/SA and scenario editing checks with absent
Canvas/Panels control identities and no initial keyboard focus grab. Existing
first canvas pan, tool selection and canvas creation clicks require no prior
activation and keep their source/model/form-lifetime checks.
- Keyboard tests traverse directly from Fit through the remaining controls to
viewport, preserving real focus, visible rings, F6 entry/exit and Inspector
typing checks.
- Retained toolbar tests capture only controls that still exist. Independent
panel-layout lifetime tests invoke the public presentation command and retain
their stale camera/gesture/source checks.
## Verification and handoff
`git diff --check` passed. No Cargo/compiler/native/Gitea operations were run.
Attempting to reuse the independent Astra/high critic failed with `agent thread
limit reached`. Root completed source review and found scope, first-click,
F6 and text ownership changes sound; commit authorized.
No executable or native acceptance is claimed.
Useful root Windows filters:
- `behavior::topology::view::layout_tests::`
- `behavior::scenario::workspace::tests::production_`
- `app::behavior_removal_tests::` for retained keyboard/text-ownership dependents
The root should inspect the native default process/scenario view, first-click
selection/tool creation, F6/Tab keyboard entry and typing after Inspector focus.
@@ -0,0 +1,21 @@
# Bound native activation fixture — source handoff
Generator: `.local/nested-call-bound-fixture-20260912.rs`. Source-only; no compiler, Cargo, native UI, Git or Gitea calls. Root owns compilation/execution and acceptance. Rustfmt only checks/formats Rust syntax; it does not establish type correctness or fixture admission.
Root's first version compiled with `-D warnings` but generation failed before writing output (exit 101). The exact failed source is preserved as `.local/native-qa/nested-bound-b250-core/probe-source.rs`, SHA256 `c5a5ce13c1910ecde13ff3e4ae9f551db521e2818ae28f4b5bded9da69c37102`, verified against `build.json` before editing. The first failed constructor was `Kind::Actor` in SA: the actual public kind is `Kind::SystemActor`. Generic `make_element(Kind::Port, ...)` would also fail intentionally; revised source now uses `ports::make_port` with explicit owner and Standard component contract. Validation, operation proof, values and whole-Project equality checks remain unchanged. Revised source awaits root's new compile/run receipts; this correction is not yet a runtime pass.
The fresh Project contains three compact scenarios, each two lifelines/four messages in exact order C1,C2,R2,R1:
| Scenario | Binding |
| --- | --- |
| OA Nested pressure checks | Two Activity lifelines; actual Interaction carrying an Information/Operation item; In `requested_pressure` and Return `measured_pressure`, both Real kPa. |
| SA Nested pressure checks | Actor/System lifelines; concrete Standard component ports; required/provided interface; synchronous InterfaceItemAllocation; BoundaryExchange carrying that operation; both Calls explicitly select that exact allocation, Replies derive it from their correlated Calls. |
| OA Reentrant pressure review | C1 L1→L2; C2 L2→L1 through an explicit reverse Interaction; R2 L1→L2; R1 L2→L1. All four remain bound to the correct channel/item and carry values. |
C1 requests `420.0` and R1 returns `418.0`; C2 requests `380.0` and R2 returns `378.0`. Every message carries one nonempty expression. IDs use the deterministic `21b00d00-0000-4000-8000-...` namespace; Call UUID order is deliberately opposite to semantic sequence order. Scenario numeric ID suffixes are 1000, 2000, and 3000; the generated TSV records actual UUID strings. Unicode `Δ pressure · 戻り値` is retained in the project description and whole-Project round trip.
Contract evidence read: `crates/oa-core/tests/source7_contract.rs` operation fixture/resolution and `operation_edit`; `crates/oa-core/src/operations.rs` exact allocation, channel, provider/caller port and reply derivation checks; `crates/oa-core/tests/r6_data.rs` OA Interaction carrying an Operation item with directional parameters/values; current `model.rs` constructors and field shapes. The OA case deliberately stores no OperationUse, which core limits to SA BoundaryInterface. It still has a real bound operation-item data contract.
Execution takes exactly one **nonexistent output directory** argument and refuses existing evidence. It validates zero Errors, resolves all four SA messages to their exact Call/allocation/ports, checks values and ordered IDs, then formats/parses through actual core and asserts complete Project equality plus canonical stability before writing. Expected completeness warnings remain recorded rather than suppressed. Outputs: `bound-nested-calls.arcadia`, `scenario-identities.tsv`, `diagnostics.txt`, and `README.md`.
Open the `.arcadia` entry and inspect the three named Behavior scenarios. Native acceptance still needs unselected/selected bar paint, exact reply ends, labels, selection, values/correlation Properties, Undo/Redo and save/reopen. Fixture admission is not activation-renderer or minimum-scale raster acceptance. No second include file is introduced: this fixture targets compact native activation behavior; comments/include preservation already has separate evaluators.
@@ -0,0 +1,527 @@
//! Source-only native fixture generator for issue 21; root compiles/runs it.
//! Uses actual oa-core validation, operation resolution, and whole-Project source round trips.
//! Usage: nested-call-bound-fixture.exe NEW_OUTPUT_DIRECTORY
use oa_core::{operations, validation, *};
use std::{fs, path::Path};
fn id(n: u128) -> Id {
Id::from_u128(0x21b00d00000040008000000000000000 + n)
}
fn element(
p: &mut Project,
layer: Layer,
n: u128,
kind: Kind,
name: &str,
parent: Option<Id>,
) -> Id {
let mut e = p
.make_element(kind, layer, name, parent)
.unwrap_or_else(|| {
panic!("No generic constructor for {layer:?} {kind:?} '{name}' (fixture ID {n})")
});
e.id = id(n);
e.alias = format!("bound_{n}");
assert!(p.elements.insert(e.id, e).is_none());
id(n)
}
fn shared(p: &mut Project, element: Id, body: SharedBody) {
*p.elements.get_mut(&element).unwrap().shared_mut().unwrap() = body;
}
fn component_port(p: &mut Project, n: u128, owner: Id, name: &str) -> Id {
let mut port = ports::make_port(
p,
id(n),
owner,
name,
PortContract::Component {
kind: ComponentPortKind::Standard,
orientation: ComponentPortOrientation::Unset,
},
)
.expect("Explicit component-port contract and owner");
port.alias = format!("bound_{n}");
assert!(p.elements.insert(port.id, port).is_none());
id(n)
}
fn relation(p: &mut Project, n: u128, name: &str, body: RelationBody) -> Id {
let r = Relation {
id: id(n),
alias: format!("bound_{n}"),
name: name.into(),
description: String::new(),
body,
};
assert!(p.relations.insert(r.id, r).is_none());
id(n)
}
fn parameter(
p: &mut Project,
layer: Layer,
n: u128,
owner: Id,
name: &str,
direction: ParameterDirection,
) -> Id {
let field = element(p, layer, n, Kind::DataField, name, Some(owner));
shared(
p,
field,
SharedBody::DataField {
type_ref: TypeRef::Real,
minimum: 1,
maximum: Some(1),
unit: "kPa".into(),
direction,
storage: FieldStorage::Value,
default: None,
},
);
field
}
#[derive(Clone, Copy)]
struct Contract {
layer: Layer,
requester: Id,
responder: Id,
channel: Id,
operation: Option<Id>,
input: Id,
output: Id,
}
fn oa_contract(p: &mut Project) -> Contract {
let layer = Layer::Oa;
let requester = element(
p,
layer,
100,
Kind::Activity,
"Review pressure demand",
None,
);
let responder = element(p, layer, 101, Kind::Activity, "Inspect pressure", None);
let item = element(p, layer, 110, Kind::ExchangeItem, "Check pressure", None);
shared(
p,
item,
SharedBody::ExchangeItem {
kind: ExchangeItemKind::Information,
mechanism: ExchangeMechanism::Operation,
supertype: None,
},
);
let input = parameter(
p,
layer,
111,
item,
"requested_pressure",
ParameterDirection::In,
);
let output = parameter(
p,
layer,
112,
item,
"measured_pressure",
ParameterDirection::Return,
);
let channel = relation(
p,
120,
"Pressure check",
RelationBody::Interaction {
source: requester,
target: responder,
items: vec![item],
},
);
Contract {
layer,
requester,
responder,
channel,
operation: None,
input,
output,
}
}
fn sa_contract(p: &mut Project) -> Contract {
let layer = Layer::Sa;
let requester = element(p, layer, 200, Kind::SystemActor, "Operator console", None);
let responder = element(p, layer, 201, Kind::System, "Pressure service", None);
let caller_port = component_port(p, 202, requester, "Client socket");
let provider_port = component_port(p, 203, responder, "Service socket");
let item = element(p, layer, 210, Kind::ExchangeItem, "Check pressure", None);
shared(
p,
item,
SharedBody::ExchangeItem {
kind: ExchangeItemKind::Information,
mechanism: ExchangeMechanism::Operation,
supertype: None,
},
);
let input = parameter(
p,
layer,
211,
item,
"requested_pressure",
ParameterDirection::In,
);
let output = parameter(
p,
layer,
212,
item,
"measured_pressure",
ParameterDirection::Return,
);
let interface = element(p, layer, 213, Kind::Interface, "Pressure check API", None);
shared(p, interface, SharedBody::Interface { supertype: None });
let allocation = relation(
p,
220,
"Synchronous pressure check",
RelationBody::InterfaceItemAllocation {
source: interface,
target: item,
order: 0,
transmission: TransmissionProtocol::Synchronous,
acquisition: AcquisitionProtocol::Unspecified,
},
);
relation(
p,
221,
"Console requires check",
RelationBody::InterfaceRequirement {
source: caller_port,
target: interface,
},
);
relation(
p,
222,
"Service provides check",
RelationBody::InterfaceProvision {
source: provider_port,
target: interface,
},
);
let channel = relation(
p,
223,
"Pressure channel",
RelationBody::BoundaryExchange {
source: caller_port,
target: provider_port,
oriented: false,
items: vec![item],
},
);
Contract {
layer,
requester,
responder,
channel,
operation: Some(allocation),
input,
output,
}
}
struct Case {
owner: Id,
caller: Id,
callee: Id,
call: Id,
inner: Id,
inner_reply: Id,
reply: Id,
}
fn message(
p: &mut Project,
contract: Contract,
n: u128,
owner: Id,
name: &str,
endpoints: (Id, Id),
reply_and_value: (Option<Id>, &str),
) -> Id {
let (reply, expression) = reply_and_value;
let message = element(p, contract.layer, n, Kind::Occurrence, name, Some(owner));
shared(
p,
message,
SharedBody::Occurrence {
body: OccurrenceBody::Exchange {
operation: if reply.is_none() {
contract.operation.map(|allocation| OperationUse {
allocation: Some(allocation),
})
} else {
None
},
exchange: Some(contract.channel),
sender: Some(endpoints.0),
receiver: Some(endpoints.1),
message_kind: if reply.is_some() {
MessageKind::Reply
} else {
MessageKind::Call
},
reply_to: reply,
values: vec![MessageValue {
field: if reply.is_some() {
contract.output
} else {
contract.input
},
path: vec![],
expression: expression.into(),
}],
},
precondition: String::new(),
postcondition: String::new(),
},
);
message
}
fn scenario(
p: &mut Project,
contract: Contract,
base: u128,
name: &str,
callback: Option<Id>,
) -> Case {
let owner = element(p, contract.layer, base, Kind::Scenario, name, None);
shared(
p,
owner,
SharedBody::Scenario {
viewpoint: if contract.layer == Layer::Oa {
ScenarioViewpoint::OperationalActivity
} else {
ScenarioViewpoint::BoundaryInterface
},
context: None,
precondition: "Two pressure checks are reviewed before the outer reply".into(),
postcondition: "Each reply retains its own requested and measured values".into(),
steps: vec![],
},
);
let caller = element(
p,
contract.layer,
base + 1,
Kind::Lifeline,
"L1 Console",
Some(owner),
);
let callee = element(
p,
contract.layer,
base + 2,
Kind::Lifeline,
"L2 Inspector",
Some(owner),
);
shared(
p,
caller,
SharedBody::Lifeline {
represented: Some(contract.requester),
},
);
shared(
p,
callee,
SharedBody::Lifeline {
represented: Some(contract.responder),
},
);
// Reverse UUID order deliberately: chronological order comes from steps.
let call = message(
p,
contract,
base + 40,
owner,
"C1 420 kPa",
(caller, callee),
(None, "420.0"),
);
let inner_contract = Contract {
channel: callback.unwrap_or(contract.channel),
..contract
};
let inner_endpoints = if callback.is_some() {
(callee, caller)
} else {
(caller, callee)
};
let inner = message(
p,
inner_contract,
base + 30,
owner,
"C2 380 kPa",
inner_endpoints,
(None, "380.0"),
);
let inner_reply = message(
p,
inner_contract,
base + 60,
owner,
"R2 378 kPa",
(inner_endpoints.1, inner_endpoints.0),
(Some(inner), "378.0"),
);
let reply = message(
p,
contract,
base + 50,
owner,
"R1 418 kPa",
(callee, caller),
(Some(call), "418.0"),
);
*p.elements.get_mut(&owner).unwrap().steps_mut().unwrap() =
vec![call, inner, inner_reply, reply];
Case {
owner,
caller,
callee,
call,
inner,
inner_reply,
reply,
}
}
fn assert_case(p: &Project, case: &Case, contract: Contract) {
assert_eq!(
p.elements[&case.owner].steps().unwrap(),
&vec![case.call, case.inner, case.inner_reply, case.reply]
);
for (message, invocation, expression) in [
(case.call, case.call, "420.0"),
(case.inner, case.inner, "380.0"),
(case.inner_reply, case.inner, "378.0"),
(case.reply, case.call, "418.0"),
] {
let Some(SharedBody::Occurrence {
body:
OccurrenceBody::Exchange {
values,
exchange,
sender,
receiver,
..
},
..
}) = p.elements[&message].shared()
else {
panic!("Missing message");
};
assert!(exchange.is_some() && sender.is_some() && receiver.is_some());
assert_eq!(values.len(), 1);
assert_eq!(values[0].expression, expression);
if let Some(allocation) = contract.operation {
let operations::OperationResolution::Selected(proof) =
operations::resolve(p, message).unwrap()
else {
panic!("Operation is not actually resolved");
};
assert_eq!(proof.invocation, invocation);
assert_eq!(proof.allocation, allocation);
assert_eq!(proof.caller_port, id(202));
assert_eq!(proof.provider_port, id(203));
} else {
assert_eq!(
operations::resolve(p, message).unwrap(),
operations::OperationResolution::Ordinary
);
}
}
}
fn main() {
let args: Vec<_> = std::env::args().collect();
assert_eq!(args.len(), 2, "Pass a NEW output directory");
let directory = Path::new(&args[1]);
assert!(
!directory.exists(),
"Refuse to replace existing native evidence"
);
let mut p = Project::empty();
p.id = id(1);
p.alias = "bound_nested_calls".into();
p.name = "Bound nested pressure checks".into();
p.description = "Native activation acceptance. Requested 420/380 kPa, measured 418/378 kPa. Unicode readback: Δ pressure · 戻り値.".into();
let oa = oa_contract(&mut p);
let sa = sa_contract(&mut p);
let oa_case = scenario(&mut p, oa, 1000, "OA Nested pressure checks", None);
let sa_case = scenario(&mut p, sa, 2000, "SA Nested pressure checks", None);
let callback = relation(
&mut p,
121,
"Demand review callback",
RelationBody::Interaction {
source: oa.responder,
target: oa.requester,
items: vec![id(110)],
},
);
let reentrant = scenario(
&mut p,
oa,
3000,
"OA Reentrant pressure review",
Some(callback),
);
let issues = validation::validate(&p);
let errors: Vec<_> = issues
.iter()
.filter(|i| i.severity == validation::Severity::Error)
.collect();
assert!(
errors.is_empty(),
"Actual core rejects fixture: {errors:#?}"
);
for (case, contract) in [(&oa_case, oa), (&sa_case, sa), (&reentrant, oa)] {
assert_case(&p, case, contract);
}
let canonical = language::format(&p);
let reparsed =
language::parse(&canonical).expect("Canonical source must parse through actual core");
assert_eq!(
reparsed, p,
"Whole-Project equality, not only ordered steps"
);
assert_eq!(language::format(&reparsed), canonical);
let mut manifest = String::from("scenario\towner\tL1\tL2\tC1\tC2\tR2\tR1\n");
for case in [&oa_case, &sa_case, &reentrant] {
manifest.push_str(&format!(
"{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
p.named(case.owner),
case.owner,
case.caller,
case.callee,
case.call,
case.inner,
case.inner_reply,
case.reply
));
}
fs::create_dir(directory).unwrap();
fs::write(directory.join("bound-nested-calls.arcadia"), canonical).unwrap();
fs::write(directory.join("scenario-identities.tsv"), manifest).unwrap();
fs::write(directory.join("diagnostics.txt"), format!("{issues:#?}\n")).unwrap();
fs::write(directory.join("README.md"), "Open bound-nested-calls.arcadia. In Behavior select OA Nested pressure checks, SA Nested pressure checks, or OA Reentrant pressure review. Each has two lifelines and four rows ordered C1,C2,R2,R1. C1 requests 420.0 kPa and R1 returns 418.0; C2 requests 380.0 and R2 returns 378.0. The SA messages resolve an actual synchronous interface-operation allocation; Replies derive it from their exact Calls. OA uses a bound Interaction carrying an Operation item without SA-only OperationUse. The reentrant case uses an explicit reverse Interaction for C2/R2. Check unselected bars, both reply ends, each bar's selection, Properties values/correlation, source parity and save/reopen. scenario-identities.tsv lists every UI target UUID. diagnostics.txt records completeness warnings. Generator validation and roundtrip are not desktop paint or native acceptance.\n").unwrap();
println!(
"PASS: {} elements, {} relations, three bound scenarios, zero Errors, {} warnings; whole-Project roundtrip and exact SA operation resolution",
p.elements.len(),
p.relations.len(),
issues.len()
);
}
@@ -0,0 +1 @@
Open bound-nested-calls.arcadia. In Behavior select OA Nested pressure checks, SA Nested pressure checks, or OA Reentrant pressure review. Each has two lifelines and four rows ordered C1,C2,R2,R1. C1 requests 420.0 kPa and R1 returns 418.0; C2 requests 380.0 and R2 returns 378.0. The SA messages resolve an actual synchronous interface-operation allocation; Replies derive it from their exact Calls. OA uses a bound Interaction carrying an Operation item without SA-only OperationUse. The reentrant case uses an explicit reverse Interaction for C2/R2. Check unselected bars, both reply ends, each bar's selection, Properties values/correlation, source parity and save/reopen. scenario-identities.tsv lists every UI target UUID. diagnostics.txt records completeness warnings. Generator validation and roundtrip are not desktop paint or native acceptance.
@@ -0,0 +1,486 @@
arcadia 7;
project bound_nested_calls "Bound nested pressure checks" id "21b00d00-0000-4000-8000-000000000001" {
description "Native activation acceptance. Requested 420/380 kPa, measured 418/378 kPa. Unicode readback: Δ pressure · 戻り値.";
oa_activity bound_100 "Review pressure demand" id "21b00d00-0000-4000-8000-000000000064" {
description "";
parent none;
}
oa_activity bound_101 "Inspect pressure" id "21b00d00-0000-4000-8000-000000000065" {
description "";
parent none;
}
oa_exchange_item bound_110 "Check pressure" id "21b00d00-0000-4000-8000-00000000006e" {
description "";
kind Information;
mechanism Operation;
parent none;
supertype none;
}
oa_data_field bound_111 "requested_pressure" id "21b00d00-0000-4000-8000-00000000006f" {
default none;
description "";
direction In;
maximum 1;
minimum 1;
parent @bound_110;
storage Value;
type_ref Real;
unit "kPa";
}
oa_data_field bound_112 "measured_pressure" id "21b00d00-0000-4000-8000-000000000070" {
default none;
description "";
direction Return;
maximum 1;
minimum 1;
parent @bound_110;
storage Value;
type_ref Real;
unit "kPa";
}
sa_actor bound_200 "Operator console" id "21b00d00-0000-4000-8000-0000000000c8" {
description "";
parent none;
}
sa_system bound_201 "Pressure service" id "21b00d00-0000-4000-8000-0000000000c9" {
description "";
parent none;
}
sa_port bound_202 "Client socket" id "21b00d00-0000-4000-8000-0000000000ca" {
contract {
Component {
kind Standard;
orientation Unset;
};
};
description "";
legacy none;
parent @bound_200;
}
sa_port bound_203 "Service socket" id "21b00d00-0000-4000-8000-0000000000cb" {
contract {
Component {
kind Standard;
orientation Unset;
};
};
description "";
legacy none;
parent @bound_201;
}
sa_exchange_item bound_210 "Check pressure" id "21b00d00-0000-4000-8000-0000000000d2" {
description "";
kind Information;
mechanism Operation;
parent none;
supertype none;
}
sa_data_field bound_211 "requested_pressure" id "21b00d00-0000-4000-8000-0000000000d3" {
default none;
description "";
direction In;
maximum 1;
minimum 1;
parent @bound_210;
storage Value;
type_ref Real;
unit "kPa";
}
sa_data_field bound_212 "measured_pressure" id "21b00d00-0000-4000-8000-0000000000d4" {
default none;
description "";
direction Return;
maximum 1;
minimum 1;
parent @bound_210;
storage Value;
type_ref Real;
unit "kPa";
}
sa_interface bound_213 "Pressure check API" id "21b00d00-0000-4000-8000-0000000000d5" {
description "";
parent none;
supertype none;
}
oa_scenario bound_1000 "OA Nested pressure checks" id "21b00d00-0000-4000-8000-0000000003e8" {
context none;
description "";
parent none;
postcondition "Each reply retains its own requested and measured values";
precondition "Two pressure checks are reviewed before the outer reply";
steps [@bound_1040, @bound_1030, @bound_1060, @bound_1050];
viewpoint OperationalActivity;
}
oa_lifeline bound_1001 "L1 Console" id "21b00d00-0000-4000-8000-0000000003e9" {
description "";
parent @bound_1000;
represented @bound_100;
}
oa_lifeline bound_1002 "L2 Inspector" id "21b00d00-0000-4000-8000-0000000003ea" {
description "";
parent @bound_1000;
represented @bound_101;
}
oa_occurrence bound_1030 "C2 380 kPa" id "21b00d00-0000-4000-8000-000000000406" {
body {
Exchange {
exchange @bound_120;
message_kind Call;
receiver @bound_1002;
reply_to none;
sender @bound_1001;
values [{
expression "380.0";
field @bound_111;
path [];
}];
};
};
description "";
parent @bound_1000;
postcondition "";
precondition "";
}
oa_occurrence bound_1040 "C1 420 kPa" id "21b00d00-0000-4000-8000-000000000410" {
body {
Exchange {
exchange @bound_120;
message_kind Call;
receiver @bound_1002;
reply_to none;
sender @bound_1001;
values [{
expression "420.0";
field @bound_111;
path [];
}];
};
};
description "";
parent @bound_1000;
postcondition "";
precondition "";
}
oa_occurrence bound_1050 "R1 418 kPa" id "21b00d00-0000-4000-8000-00000000041a" {
body {
Exchange {
exchange @bound_120;
message_kind Reply;
receiver @bound_1001;
reply_to @bound_1040;
sender @bound_1002;
values [{
expression "418.0";
field @bound_112;
path [];
}];
};
};
description "";
parent @bound_1000;
postcondition "";
precondition "";
}
oa_occurrence bound_1060 "R2 378 kPa" id "21b00d00-0000-4000-8000-000000000424" {
body {
Exchange {
exchange @bound_120;
message_kind Reply;
receiver @bound_1001;
reply_to @bound_1030;
sender @bound_1002;
values [{
expression "378.0";
field @bound_112;
path [];
}];
};
};
description "";
parent @bound_1000;
postcondition "";
precondition "";
}
sa_scenario bound_2000 "SA Nested pressure checks" id "21b00d00-0000-4000-8000-0000000007d0" {
context none;
description "";
parent none;
postcondition "Each reply retains its own requested and measured values";
precondition "Two pressure checks are reviewed before the outer reply";
steps [@bound_2040, @bound_2030, @bound_2060, @bound_2050];
viewpoint BoundaryInterface;
}
sa_lifeline bound_2001 "L1 Console" id "21b00d00-0000-4000-8000-0000000007d1" {
description "";
parent @bound_2000;
represented @bound_200;
}
sa_lifeline bound_2002 "L2 Inspector" id "21b00d00-0000-4000-8000-0000000007d2" {
description "";
parent @bound_2000;
represented @bound_201;
}
sa_occurrence bound_2030 "C2 380 kPa" id "21b00d00-0000-4000-8000-0000000007ee" {
body {
Exchange {
exchange @bound_223;
message_kind Call;
operation {
allocation @bound_220;
};
receiver @bound_2002;
reply_to none;
sender @bound_2001;
values [{
expression "380.0";
field @bound_211;
path [];
}];
};
};
description "";
parent @bound_2000;
postcondition "";
precondition "";
}
sa_occurrence bound_2040 "C1 420 kPa" id "21b00d00-0000-4000-8000-0000000007f8" {
body {
Exchange {
exchange @bound_223;
message_kind Call;
operation {
allocation @bound_220;
};
receiver @bound_2002;
reply_to none;
sender @bound_2001;
values [{
expression "420.0";
field @bound_211;
path [];
}];
};
};
description "";
parent @bound_2000;
postcondition "";
precondition "";
}
sa_occurrence bound_2050 "R1 418 kPa" id "21b00d00-0000-4000-8000-000000000802" {
body {
Exchange {
exchange @bound_223;
message_kind Reply;
receiver @bound_2001;
reply_to @bound_2040;
sender @bound_2002;
values [{
expression "418.0";
field @bound_212;
path [];
}];
};
};
description "";
parent @bound_2000;
postcondition "";
precondition "";
}
sa_occurrence bound_2060 "R2 378 kPa" id "21b00d00-0000-4000-8000-00000000080c" {
body {
Exchange {
exchange @bound_223;
message_kind Reply;
receiver @bound_2001;
reply_to @bound_2030;
sender @bound_2002;
values [{
expression "378.0";
field @bound_212;
path [];
}];
};
};
description "";
parent @bound_2000;
postcondition "";
precondition "";
}
oa_scenario bound_3000 "OA Reentrant pressure review" id "21b00d00-0000-4000-8000-000000000bb8" {
context none;
description "";
parent none;
postcondition "Each reply retains its own requested and measured values";
precondition "Two pressure checks are reviewed before the outer reply";
steps [@bound_3040, @bound_3030, @bound_3060, @bound_3050];
viewpoint OperationalActivity;
}
oa_lifeline bound_3001 "L1 Console" id "21b00d00-0000-4000-8000-000000000bb9" {
description "";
parent @bound_3000;
represented @bound_100;
}
oa_lifeline bound_3002 "L2 Inspector" id "21b00d00-0000-4000-8000-000000000bba" {
description "";
parent @bound_3000;
represented @bound_101;
}
oa_occurrence bound_3030 "C2 380 kPa" id "21b00d00-0000-4000-8000-000000000bd6" {
body {
Exchange {
exchange @bound_121;
message_kind Call;
receiver @bound_3001;
reply_to none;
sender @bound_3002;
values [{
expression "380.0";
field @bound_111;
path [];
}];
};
};
description "";
parent @bound_3000;
postcondition "";
precondition "";
}
oa_occurrence bound_3040 "C1 420 kPa" id "21b00d00-0000-4000-8000-000000000be0" {
body {
Exchange {
exchange @bound_120;
message_kind Call;
receiver @bound_3002;
reply_to none;
sender @bound_3001;
values [{
expression "420.0";
field @bound_111;
path [];
}];
};
};
description "";
parent @bound_3000;
postcondition "";
precondition "";
}
oa_occurrence bound_3050 "R1 418 kPa" id "21b00d00-0000-4000-8000-000000000bea" {
body {
Exchange {
exchange @bound_120;
message_kind Reply;
receiver @bound_3001;
reply_to @bound_3040;
sender @bound_3002;
values [{
expression "418.0";
field @bound_112;
path [];
}];
};
};
description "";
parent @bound_3000;
postcondition "";
precondition "";
}
oa_occurrence bound_3060 "R2 378 kPa" id "21b00d00-0000-4000-8000-000000000bf4" {
body {
Exchange {
exchange @bound_121;
message_kind Reply;
receiver @bound_3002;
reply_to @bound_3030;
sender @bound_3001;
values [{
expression "378.0";
field @bound_112;
path [];
}];
};
};
description "";
parent @bound_3000;
postcondition "";
precondition "";
}
interaction bound_120 "Pressure check" id "21b00d00-0000-4000-8000-000000000078" {
description "";
items [@bound_110];
source @bound_100;
target @bound_101;
}
interaction bound_121 "Demand review callback" id "21b00d00-0000-4000-8000-000000000079" {
description "";
items [@bound_110];
source @bound_101;
target @bound_100;
}
interface_item_allocation bound_220 "Synchronous pressure check" id "21b00d00-0000-4000-8000-0000000000dc" {
acquisition Unspecified;
description "";
order 0;
source @bound_213;
target @bound_210;
transmission Synchronous;
}
interface_requirement bound_221 "Console requires check" id "21b00d00-0000-4000-8000-0000000000dd" {
description "";
source @bound_202;
target @bound_213;
}
interface_provision bound_222 "Service provides check" id "21b00d00-0000-4000-8000-0000000000de" {
description "";
source @bound_203;
target @bound_213;
}
boundary_exchange bound_223 "Pressure channel" id "21b00d00-0000-4000-8000-0000000000df" {
description "";
items [@bound_210];
oriented false;
source @bound_202;
target @bound_203;
}
}
@@ -0,0 +1,24 @@
[
Issue {
severity: Warning,
element: Some(
21b00d00-0000-4000-8000-000000000064,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Review pressure demand' to a performer.",
},
Issue {
severity: Warning,
element: Some(
21b00d00-0000-4000-8000-000000000065,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Inspect pressure' to a performer.",
},
]
@@ -0,0 +1,4 @@
scenario owner L1 L2 C1 C2 R2 R1
OA Nested pressure checks 21b00d00-0000-4000-8000-0000000003e8 21b00d00-0000-4000-8000-0000000003e9 21b00d00-0000-4000-8000-0000000003ea 21b00d00-0000-4000-8000-000000000410 21b00d00-0000-4000-8000-000000000406 21b00d00-0000-4000-8000-000000000424 21b00d00-0000-4000-8000-00000000041a
SA Nested pressure checks 21b00d00-0000-4000-8000-0000000007d0 21b00d00-0000-4000-8000-0000000007d1 21b00d00-0000-4000-8000-0000000007d2 21b00d00-0000-4000-8000-0000000007f8 21b00d00-0000-4000-8000-0000000007ee 21b00d00-0000-4000-8000-00000000080c 21b00d00-0000-4000-8000-000000000802
OA Reentrant pressure review 21b00d00-0000-4000-8000-000000000bb8 21b00d00-0000-4000-8000-000000000bb9 21b00d00-0000-4000-8000-000000000bba 21b00d00-0000-4000-8000-000000000be0 21b00d00-0000-4000-8000-000000000bd6 21b00d00-0000-4000-8000-000000000bf4 21b00d00-0000-4000-8000-000000000bea
1 scenario owner L1 L2 C1 C2 R2 R1
2 OA Nested pressure checks 21b00d00-0000-4000-8000-0000000003e8 21b00d00-0000-4000-8000-0000000003e9 21b00d00-0000-4000-8000-0000000003ea 21b00d00-0000-4000-8000-000000000410 21b00d00-0000-4000-8000-000000000406 21b00d00-0000-4000-8000-000000000424 21b00d00-0000-4000-8000-00000000041a
3 SA Nested pressure checks 21b00d00-0000-4000-8000-0000000007d0 21b00d00-0000-4000-8000-0000000007d1 21b00d00-0000-4000-8000-0000000007d2 21b00d00-0000-4000-8000-0000000007f8 21b00d00-0000-4000-8000-0000000007ee 21b00d00-0000-4000-8000-00000000080c 21b00d00-0000-4000-8000-000000000802
4 OA Reentrant pressure review 21b00d00-0000-4000-8000-000000000bb8 21b00d00-0000-4000-8000-000000000bb9 21b00d00-0000-4000-8000-000000000bba 21b00d00-0000-4000-8000-000000000be0 21b00d00-0000-4000-8000-000000000bd6 21b00d00-0000-4000-8000-000000000bf4 21b00d00-0000-4000-8000-000000000bea
@@ -0,0 +1,107 @@
//! Actual oa-core fixtures for issue 21. Source-only contribution; NOT compiled/run.
//! Link to the current oa_core rlib, then run with a NEW output directory argument.
//! Draw/paint/hit verification belongs to the production desktop, not this probe.
use oa_core::*;
use std::{fs, path::Path};
fn id(n: u128) -> Id { Id::from_u128(0x21ca1100000040008000000000000000 + n) }
fn seed(p: &mut Project, layer: Layer, n: u128, kind: Kind, name: &str, parent: Option<Id>) -> Id {
let mut e = p.make_element(kind, layer, name, parent).unwrap();
e.id = id(n);
e.alias = format!("activation_{n}");
let result = e.id;
assert!(p.elements.insert(result, e).is_none());
result
}
fn occurrence(p: &mut Project, layer: Layer, n: u128, name: &str, parent: Id, body: OccurrenceBody) -> Id {
let result = seed(p, layer, n, Kind::Occurrence, name, Some(parent));
*p.elements.get_mut(&result).unwrap().shared_mut().unwrap() = SharedBody::Occurrence {
body, precondition: String::new(), postcondition: String::new(),
};
result
}
fn message(p: &mut Project, layer: Layer, n: u128, name: &str, parent: Id, sender: Id, receiver: Id, reply: Option<Id>) -> Id {
occurrence(p, layer, n, name, parent, OccurrenceBody::Exchange {
operation: None, exchange: None, sender: Some(sender), receiver: Some(receiver),
message_kind: if reply.is_some() { MessageKind::Reply } else { MessageKind::Call },
reply_to: reply, values: vec![],
})
}
fn steps(p: &mut Project, owner: Id, values: Vec<Id>) {
*p.elements.get_mut(&owner).unwrap().steps_mut().unwrap() = values;
}
fn main() {
let args: Vec<_> = std::env::args().collect();
assert_eq!(args.len(), 2, "Pass a NEW output directory");
let directory = Path::new(&args[1]);
assert!(!directory.exists(), "Preserve existing evidence; use a fresh directory");
let mut p = Project::empty();
p.id = id(1);
p.alias = "nested_call_evaluator".into();
p.name = "Nested call activation evaluator".into();
p.description = "Draft exchange references intentionally absent; exact lifelines and correlations isolate receiver activation geometry.".into();
for (side, layer) in [(0u128, Layer::Oa), (1, Layer::Sa)] {
let base = 10000 + side * 10000;
let work_kind = if layer == Layer::Oa { Kind::Activity } else { Kind::Function };
let a = seed(&mut p, layer, base, work_kind, "Caller work", None);
let b = seed(&mut p, layer, base + 1, work_kind, "Callee work", None);
for (case_index, case) in ["nested", "sequential", "crossed", "self", "reentrant", "open", "alt", "parallel"].iter().enumerate() {
let n = base + 100 + case_index as u128 * 100;
let owner = seed(&mut p, layer, n, Kind::Scenario, &format!("{layer:?} {case}"), None);
if let SharedBody::Scenario { viewpoint, .. } = p.elements.get_mut(&owner).unwrap().shared_mut().unwrap() {
*viewpoint = if layer == Layer::Oa { ScenarioViewpoint::OperationalActivity } else { ScenarioViewpoint::Functional };
}
let l1 = seed(&mut p, layer, n + 1, Kind::Lifeline, "L1 Caller", Some(owner));
let l2 = seed(&mut p, layer, n + 2, Kind::Lifeline, "L2 Callee", Some(owner));
for (line, represented) in [(l1, a), (l2, b)] {
*p.elements.get_mut(&line).unwrap().shared_mut().unwrap() = SharedBody::Lifeline { represented: Some(represented) };
}
// Deliberately reverse Call UUID order compared with semantic order.
let c1 = message(&mut p, layer, n + 40, "Call 1", owner, l1, l2, None);
if *case == "alt" || *case == "parallel" {
let operator = if *case == "alt" { FragmentOperator::Alternative } else { FragmentOperator::Parallel };
let fragment = occurrence(&mut p, layer, n + 10, case, owner, OccurrenceBody::Fragment { operator, branches: vec![id(n + 11), id(n + 12)] });
for branch_index in 0..2u128 {
let branch = occurrence(&mut p, layer, n + 11 + branch_index, &format!("Operand {branch_index}"), fragment, OccurrenceBody::Branch {
guard: format!("condition_{branch_index}"), occurrences: vec![],
});
let local = message(&mut p, layer, n + 20 + branch_index * 3, "Branch Call", branch, l1, l2, None);
let local_reply = message(&mut p, layer, n + 21 + branch_index * 3, "Branch Reply", branch, l2, l1, Some(local));
let mut branch_steps = vec![local, local_reply];
if *case == "alt" && branch_index == 0 {
let reply = message(&mut p, layer, n + 70, "Conditional outer Reply", branch, l2, l1, Some(c1));
branch_steps.push(reply);
}
steps(&mut p, branch, branch_steps);
}
steps(&mut p, owner, vec![c1, fragment]);
continue;
}
let (from, to) = match *case { "self" => (l2, l2), "reentrant" => (l2, l1), _ => (l1, l2) };
let c2 = message(&mut p, layer, n + 30, "Call 2", owner, from, to, None);
let r2 = message(&mut p, layer, n + 60, "Reply 2 to Call 2", owner, to, from, Some(c2));
let r1 = message(&mut p, layer, n + 50, "Reply 1 to Call 1", owner, l2, l1, Some(c1));
let ordered = match *case {
"sequential" => vec![c1, r1, c2, r2],
"crossed" => vec![c1, c2, r1, r2],
"open" => { p.elements.remove(&r1); vec![c1, c2, r2] },
_ => vec![c1, c2, r2, r1],
};
steps(&mut p, owner, ordered);
}
}
let issues = validation::validate(&p);
let errors: Vec<_> = issues.iter().filter(|i| i.severity == validation::Severity::Error).collect();
assert!(errors.is_empty(), "Core fixture errors: {errors:#?}");
let canonical = language::format(&p);
let reparsed = language::parse(&canonical).expect("Actual canonical source must parse");
assert_eq!(language::format(&reparsed), canonical, "Canonical source round trip");
assert_eq!(reparsed.elements.len(), p.elements.len());
for (uuid, element) in &p.elements {
assert_eq!(reparsed.elements[uuid].steps(), element.steps(), "Exact ordered steps for {uuid}");
}
fs::create_dir_all(directory).unwrap();
fs::write(directory.join("nested-call-evaluator.arcadia"), canonical).unwrap();
fs::write(directory.join("diagnostics.txt"), format!("{issues:#?}\n")).unwrap();
println!("PASS actual core validation and canonical round trip: {} elements, {} warnings; no desktop draw/hit/runtime claim", p.elements.len(), issues.len());
}
@@ -0,0 +1,132 @@
[
{
"file": "activation-design.md",
"bytes": 14414,
"sha256": "039ee45a46072697cfd05aa582b248dc6da22a17e93188a6aaaa55b5e8690501"
},
{
"file": "activation-implementation.md",
"bytes": 3849,
"sha256": "245825817aa154ff4f46efa78b4bc8ff3f17420246a5a5fd4dd439a7a3f8fcc2"
},
{
"file": "draft-fixture.rs",
"bytes": 6789,
"sha256": "e296ad928a4e01321d20cbcfb046a64a733561cf6f7a07d8d52eaeb64d4e4ee2"
},
{
"file": "bound-fixture.rs",
"bytes": 15775,
"sha256": "d74a482d6433a8e0d8e82ae67eaca1b28df92920466bbb247ffac0bd1cd80928"
},
{
"file": "bound-fixture-preparation.md",
"bytes": 3942,
"sha256": "5f045603cbf312ba3d75dd2751c01082be3a894e33f2d88a94b43cda0eaa2efb"
},
{
"file": "state-focus-review.md",
"bytes": 8522,
"sha256": "a6e376732a7bebe65ed2779dcb74a63aef8e2dc28de8aa54a1e0c79edf378b8f"
},
{
"file": "always-active-review.md",
"bytes": 3060,
"sha256": "ebafb01d9c2c846128d065745a616a237b073c841ebd8c9bcde8c8c77b9e53a9"
},
{
"file": "nested-call-f7-core/build.json",
"bytes": 2090,
"sha256": "545fff7f846cd5cfe575d9a5b66b6ccf93abab799f9a70f8fbeb8a4e006d67c6"
},
{
"file": "nested-call-f7-core/build.log",
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"file": "nested-call-f7-core/fixtures.json",
"bytes": 340,
"sha256": "903ed16d6a118535a89b0f1ba2c4a59c6e8eb0e1f7e97df3828272b98b8a90c6"
},
{
"file": "nested-call-f7-core/fixtures.log",
"bytes": 115,
"sha256": "6df65352be448872b2485700615fec64e898f37427572b9cade519337f0693e2"
},
{
"file": "nested-bound-b250-core/build.json",
"bytes": 2113,
"sha256": "eb423b32a6ed5fd6c544fb608d1dff7c552368f6aa5b6067598db791fe37160e"
},
{
"file": "nested-bound-b250-core/build.log",
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"file": "nested-bound-b250-core/generate.json",
"bytes": 349,
"sha256": "36a6672f5df816de3263316d51e7d924f6fa06683bfc3d9ea958f44406b8db37"
},
{
"file": "nested-bound-b250-core/generate.log",
"bytes": 251,
"sha256": "5fd962fe347b06caf5c089ae7fad37b09bc832bf6b2cc8a1db9130fbecadd77a"
},
{
"file": "nested-bound-b250-core/probe-source.rs",
"bytes": 15562,
"sha256": "c5a5ce13c1910ecde13ff3e4ae9f551db521e2818ae28f4b5bded9da69c37102"
},
{
"file": "nested-bound-r1-b250-core/build.json",
"bytes": 2125,
"sha256": "ae7cde0e85fdd89730a8cb8679321415b6f350778775746a866f803f57060412"
},
{
"file": "nested-bound-r1-b250-core/build.log",
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"file": "nested-bound-r1-b250-core/generate.json",
"bytes": 356,
"sha256": "b076e404b91faee4e8e504f27c8614647b80dfdd4f3a476fe19ed0c569fd4daa"
},
{
"file": "nested-bound-r1-b250-core/generate.log",
"bytes": 138,
"sha256": "a0b3983820c29ec95d451e15ccb02f05a18518b8f8204b984c2738dd3ee2ce91"
},
{
"file": "nested-call-fixture-20260912/diagnostics.txt",
"bytes": 20426,
"sha256": "2651c90057ba8ce46b9331a0fb05206e3d744928d40c63232775123282c52698"
},
{
"file": "nested-call-fixture-20260912/nested-call-evaluator.arcadia",
"bytes": 50095,
"sha256": "ee8eec6459f28a11dcd25736e3147d6a0e153e22ecc8a4c8a3c61e4494ca2835"
},
{
"file": "bound-nested-fixture-r1-20260912/bound-nested-calls.arcadia",
"bytes": 14219,
"sha256": "7531ec264ad3f4b343997e3c0b0ac3b539163908b117e32197876decf3dee3dd"
},
{
"file": "bound-nested-fixture-r1-20260912/diagnostics.txt",
"bytes": 592,
"sha256": "e2df3656e0fe6185465aced67f6649fc02bd6e584e7cb38b51ad226c9624da00"
},
{
"file": "bound-nested-fixture-r1-20260912/README.md",
"bytes": 855,
"sha256": "f5b4e5090ecf28b230bb96f24c58c0566542aae1032c32bbba058aa4eaf79ac5"
},
{
"file": "bound-nested-fixture-r1-20260912/scenario-identities.tsv",
"bytes": 891,
"sha256": "fa9cd8bd17a0d16d70e72e76a670e060e64d92ca3bd4a5bd6b92a363393108b0"
}
]
@@ -0,0 +1,52 @@
{
"source": "42511f0f8ae46f29e11ae18c1030face654c2994",
"current_head": "b25065c442fd8decd3419a94d33125cafbf9a22e",
"scope": "Core-only standalone probe; core/configuration/lock inputs match identified full test run. Desktop changes do not become core evidence.",
"start": "2026-09-12T14:11:41.203627+00:00",
"end": "2026-09-12T14:12:20.356027+00:00",
"command": [
"rustc",
"--edition=2024",
"-D",
"warnings",
"-C",
"linker=clang.exe",
"-L",
"dependency=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps",
"--extern",
"serde_json=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"--extern",
"oa_core=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\nested-call-bound-fixture-20260912.rs",
"-o",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-bound-b250-core\\nested-bound-b250-core.exe"
],
"exit": 0,
"files": [
{
"path": ".local\\nested-call-bound-fixture-20260912.rs",
"bytes": 15562,
"sha256": "c5a5ce13c1910ecde13ff3e4ae9f551db521e2818ae28f4b5bded9da69c37102"
},
{
"path": "target\\debug\\.fingerprint\\oa-desktop-e358a5ed59b1de41\\test-bin-arcadia-studio.json",
"bytes": 1079,
"sha256": "cbf2edff63ac3601b264065345b7c77e456ed8e34b09ebd204d5785d2324a2dd"
},
{
"path": "target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"bytes": 2991440,
"sha256": "485300d4125a69de7da5413f832eb4b9cde547e60f103f3b284e49aee4194d05"
},
{
"path": "target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"bytes": 102820446,
"sha256": "d395dbe1f761d41bce3cb850f44cdc0343b0db172bd03abf89e455c44f4b7e86"
},
{
"path": ".local\\native-qa\\nested-bound-b250-core\\nested-bound-b250-core.exe",
"bytes": 18493952,
"sha256": "994a78c151c9240207bbaca8ce2807b2799947382fb63e4d1ff7e47375790a36"
}
]
}
@@ -0,0 +1,9 @@
{
"utc": "2026-09-12T14:12:53.234934+00:00",
"command": [
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-bound-b250-core\\nested-bound-b250-core.exe",
".local/native-qa/bound-nested-fixture-20260912"
],
"binary_sha256": "994a78c151c9240207bbaca8ce2807b2799947382fb63e4d1ff7e47375790a36",
"exit": 101
}
@@ -0,0 +1,4 @@
thread 'main' (8772) panicked at C:\Users\User\Documents\Git\ARCADIA-Studio\.local\nested-call-bound-fixture-20260912.rs:18:59:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
@@ -0,0 +1,520 @@
//! Source-only native fixture generator for issue 21; root compiles/runs it.
//! Uses actual oa-core validation, operation resolution, and whole-Project source round trips.
//! Usage: nested-call-bound-fixture.exe NEW_OUTPUT_DIRECTORY
use oa_core::{operations, validation, *};
use std::{fs, path::Path};
fn id(n: u128) -> Id {
Id::from_u128(0x21b00d00000040008000000000000000 + n)
}
fn element(
p: &mut Project,
layer: Layer,
n: u128,
kind: Kind,
name: &str,
parent: Option<Id>,
) -> Id {
let mut e = p.make_element(kind, layer, name, parent).unwrap();
e.id = id(n);
e.alias = format!("bound_{n}");
assert!(p.elements.insert(e.id, e).is_none());
id(n)
}
fn shared(p: &mut Project, element: Id, body: SharedBody) {
*p.elements.get_mut(&element).unwrap().shared_mut().unwrap() = body;
}
fn relation(p: &mut Project, n: u128, name: &str, body: RelationBody) -> Id {
let r = Relation {
id: id(n),
alias: format!("bound_{n}"),
name: name.into(),
description: String::new(),
body,
};
assert!(p.relations.insert(r.id, r).is_none());
id(n)
}
fn parameter(
p: &mut Project,
layer: Layer,
n: u128,
owner: Id,
name: &str,
direction: ParameterDirection,
) -> Id {
let field = element(p, layer, n, Kind::DataField, name, Some(owner));
shared(
p,
field,
SharedBody::DataField {
type_ref: TypeRef::Real,
minimum: 1,
maximum: Some(1),
unit: "kPa".into(),
direction,
storage: FieldStorage::Value,
default: None,
},
);
field
}
#[derive(Clone, Copy)]
struct Contract {
layer: Layer,
requester: Id,
responder: Id,
channel: Id,
operation: Option<Id>,
input: Id,
output: Id,
}
fn oa_contract(p: &mut Project) -> Contract {
let layer = Layer::Oa;
let requester = element(
p,
layer,
100,
Kind::Activity,
"Review pressure demand",
None,
);
let responder = element(p, layer, 101, Kind::Activity, "Inspect pressure", None);
let item = element(p, layer, 110, Kind::ExchangeItem, "Check pressure", None);
shared(
p,
item,
SharedBody::ExchangeItem {
kind: ExchangeItemKind::Information,
mechanism: ExchangeMechanism::Operation,
supertype: None,
},
);
let input = parameter(
p,
layer,
111,
item,
"requested_pressure",
ParameterDirection::In,
);
let output = parameter(
p,
layer,
112,
item,
"measured_pressure",
ParameterDirection::Return,
);
let channel = relation(
p,
120,
"Pressure check",
RelationBody::Interaction {
source: requester,
target: responder,
items: vec![item],
},
);
Contract {
layer,
requester,
responder,
channel,
operation: None,
input,
output,
}
}
fn sa_contract(p: &mut Project) -> Contract {
let layer = Layer::Sa;
let requester = element(p, layer, 200, Kind::Actor, "Operator console", None);
let responder = element(p, layer, 201, Kind::System, "Pressure service", None);
let caller_port = element(p, layer, 202, Kind::Port, "Client socket", Some(requester));
let provider_port = element(p, layer, 203, Kind::Port, "Service socket", Some(responder));
for port in [caller_port, provider_port] {
shared(
p,
port,
SharedBody::Port {
contract: PortContract::Component {
kind: ComponentPortKind::Standard,
orientation: ComponentPortOrientation::Unset,
},
legacy: None,
},
);
}
let item = element(p, layer, 210, Kind::ExchangeItem, "Check pressure", None);
shared(
p,
item,
SharedBody::ExchangeItem {
kind: ExchangeItemKind::Information,
mechanism: ExchangeMechanism::Operation,
supertype: None,
},
);
let input = parameter(
p,
layer,
211,
item,
"requested_pressure",
ParameterDirection::In,
);
let output = parameter(
p,
layer,
212,
item,
"measured_pressure",
ParameterDirection::Return,
);
let interface = element(p, layer, 213, Kind::Interface, "Pressure check API", None);
shared(p, interface, SharedBody::Interface { supertype: None });
let allocation = relation(
p,
220,
"Synchronous pressure check",
RelationBody::InterfaceItemAllocation {
source: interface,
target: item,
order: 0,
transmission: TransmissionProtocol::Synchronous,
acquisition: AcquisitionProtocol::Unspecified,
},
);
relation(
p,
221,
"Console requires check",
RelationBody::InterfaceRequirement {
source: caller_port,
target: interface,
},
);
relation(
p,
222,
"Service provides check",
RelationBody::InterfaceProvision {
source: provider_port,
target: interface,
},
);
let channel = relation(
p,
223,
"Pressure channel",
RelationBody::BoundaryExchange {
source: caller_port,
target: provider_port,
oriented: false,
items: vec![item],
},
);
Contract {
layer,
requester,
responder,
channel,
operation: Some(allocation),
input,
output,
}
}
struct Case {
owner: Id,
caller: Id,
callee: Id,
call: Id,
inner: Id,
inner_reply: Id,
reply: Id,
}
fn message(
p: &mut Project,
contract: Contract,
n: u128,
owner: Id,
name: &str,
endpoints: (Id, Id),
reply_and_value: (Option<Id>, &str),
) -> Id {
let (reply, expression) = reply_and_value;
let message = element(p, contract.layer, n, Kind::Occurrence, name, Some(owner));
shared(
p,
message,
SharedBody::Occurrence {
body: OccurrenceBody::Exchange {
operation: if reply.is_none() {
contract.operation.map(|allocation| OperationUse {
allocation: Some(allocation),
})
} else {
None
},
exchange: Some(contract.channel),
sender: Some(endpoints.0),
receiver: Some(endpoints.1),
message_kind: if reply.is_some() {
MessageKind::Reply
} else {
MessageKind::Call
},
reply_to: reply,
values: vec![MessageValue {
field: if reply.is_some() {
contract.output
} else {
contract.input
},
path: vec![],
expression: expression.into(),
}],
},
precondition: String::new(),
postcondition: String::new(),
},
);
message
}
fn scenario(
p: &mut Project,
contract: Contract,
base: u128,
name: &str,
callback: Option<Id>,
) -> Case {
let owner = element(p, contract.layer, base, Kind::Scenario, name, None);
shared(
p,
owner,
SharedBody::Scenario {
viewpoint: if contract.layer == Layer::Oa {
ScenarioViewpoint::OperationalActivity
} else {
ScenarioViewpoint::BoundaryInterface
},
context: None,
precondition: "Two pressure checks are reviewed before the outer reply".into(),
postcondition: "Each reply retains its own requested and measured values".into(),
steps: vec![],
},
);
let caller = element(
p,
contract.layer,
base + 1,
Kind::Lifeline,
"L1 Console",
Some(owner),
);
let callee = element(
p,
contract.layer,
base + 2,
Kind::Lifeline,
"L2 Inspector",
Some(owner),
);
shared(
p,
caller,
SharedBody::Lifeline {
represented: Some(contract.requester),
},
);
shared(
p,
callee,
SharedBody::Lifeline {
represented: Some(contract.responder),
},
);
// Reverse UUID order deliberately: chronological order comes from steps.
let call = message(
p,
contract,
base + 40,
owner,
"C1 420 kPa",
(caller, callee),
(None, "420.0"),
);
let inner_contract = Contract {
channel: callback.unwrap_or(contract.channel),
..contract
};
let inner_endpoints = if callback.is_some() {
(callee, caller)
} else {
(caller, callee)
};
let inner = message(
p,
inner_contract,
base + 30,
owner,
"C2 380 kPa",
inner_endpoints,
(None, "380.0"),
);
let inner_reply = message(
p,
inner_contract,
base + 60,
owner,
"R2 378 kPa",
(inner_endpoints.1, inner_endpoints.0),
(Some(inner), "378.0"),
);
let reply = message(
p,
contract,
base + 50,
owner,
"R1 418 kPa",
(callee, caller),
(Some(call), "418.0"),
);
*p.elements.get_mut(&owner).unwrap().steps_mut().unwrap() =
vec![call, inner, inner_reply, reply];
Case {
owner,
caller,
callee,
call,
inner,
inner_reply,
reply,
}
}
fn assert_case(p: &Project, case: &Case, contract: Contract) {
assert_eq!(
p.elements[&case.owner].steps().unwrap(),
&vec![case.call, case.inner, case.inner_reply, case.reply]
);
for (message, invocation, expression) in [
(case.call, case.call, "420.0"),
(case.inner, case.inner, "380.0"),
(case.inner_reply, case.inner, "378.0"),
(case.reply, case.call, "418.0"),
] {
let Some(SharedBody::Occurrence {
body:
OccurrenceBody::Exchange {
values,
exchange,
sender,
receiver,
..
},
..
}) = p.elements[&message].shared()
else {
panic!("Missing message");
};
assert!(exchange.is_some() && sender.is_some() && receiver.is_some());
assert_eq!(values.len(), 1);
assert_eq!(values[0].expression, expression);
if let Some(allocation) = contract.operation {
let operations::OperationResolution::Selected(proof) =
operations::resolve(p, message).unwrap()
else {
panic!("Operation is not actually resolved");
};
assert_eq!(proof.invocation, invocation);
assert_eq!(proof.allocation, allocation);
assert_eq!(proof.caller_port, id(202));
assert_eq!(proof.provider_port, id(203));
} else {
assert_eq!(
operations::resolve(p, message).unwrap(),
operations::OperationResolution::Ordinary
);
}
}
}
fn main() {
let args: Vec<_> = std::env::args().collect();
assert_eq!(args.len(), 2, "Pass a NEW output directory");
let directory = Path::new(&args[1]);
assert!(
!directory.exists(),
"Refuse to replace existing native evidence"
);
let mut p = Project::empty();
p.id = id(1);
p.alias = "bound_nested_calls".into();
p.name = "Bound nested pressure checks".into();
p.description = "Native activation acceptance. Requested 420/380 kPa, measured 418/378 kPa. Unicode readback: Δ pressure · 戻り値.".into();
let oa = oa_contract(&mut p);
let sa = sa_contract(&mut p);
let oa_case = scenario(&mut p, oa, 1000, "OA Nested pressure checks", None);
let sa_case = scenario(&mut p, sa, 2000, "SA Nested pressure checks", None);
let callback = relation(
&mut p,
121,
"Demand review callback",
RelationBody::Interaction {
source: oa.responder,
target: oa.requester,
items: vec![id(110)],
},
);
let reentrant = scenario(
&mut p,
oa,
3000,
"OA Reentrant pressure review",
Some(callback),
);
let issues = validation::validate(&p);
let errors: Vec<_> = issues
.iter()
.filter(|i| i.severity == validation::Severity::Error)
.collect();
assert!(
errors.is_empty(),
"Actual core rejects fixture: {errors:#?}"
);
for (case, contract) in [(&oa_case, oa), (&sa_case, sa), (&reentrant, oa)] {
assert_case(&p, case, contract);
}
let canonical = language::format(&p);
let reparsed =
language::parse(&canonical).expect("Canonical source must parse through actual core");
assert_eq!(
reparsed, p,
"Whole-Project equality, not only ordered steps"
);
assert_eq!(language::format(&reparsed), canonical);
let mut manifest = String::from("scenario\towner\tL1\tL2\tC1\tC2\tR2\tR1\n");
for case in [&oa_case, &sa_case, &reentrant] {
manifest.push_str(&format!(
"{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
p.named(case.owner),
case.owner,
case.caller,
case.callee,
case.call,
case.inner,
case.inner_reply,
case.reply
));
}
fs::create_dir(directory).unwrap();
fs::write(directory.join("bound-nested-calls.arcadia"), canonical).unwrap();
fs::write(directory.join("scenario-identities.tsv"), manifest).unwrap();
fs::write(directory.join("diagnostics.txt"), format!("{issues:#?}\n")).unwrap();
fs::write(directory.join("README.md"), "Open bound-nested-calls.arcadia. In Behavior select OA Nested pressure checks, SA Nested pressure checks, or OA Reentrant pressure review. Each has two lifelines and four rows ordered C1,C2,R2,R1. C1 requests 420.0 kPa and R1 returns 418.0; C2 requests 380.0 and R2 returns 378.0. The SA messages resolve an actual synchronous interface-operation allocation; Replies derive it from their exact Calls. OA uses a bound Interaction carrying an Operation item without SA-only OperationUse. The reentrant case uses an explicit reverse Interaction for C2/R2. Check unselected bars, both reply ends, each bar's selection, Properties values/correlation, source parity and save/reopen. scenario-identities.tsv lists every UI target UUID. diagnostics.txt records completeness warnings. Generator validation and roundtrip are not desktop paint or native acceptance.\n").unwrap();
println!(
"PASS: {} elements, {} relations, three bound scenarios, zero Errors, {} warnings; whole-Project roundtrip and exact SA operation resolution",
p.elements.len(),
p.relations.len(),
issues.len()
);
}
@@ -0,0 +1,52 @@
{
"source": "42511f0f8ae46f29e11ae18c1030face654c2994",
"current_head": "b25065c442fd8decd3419a94d33125cafbf9a22e",
"scope": "Core-only standalone probe; core/configuration/lock inputs match identified full test run. Desktop changes do not become core evidence.",
"start": "2026-09-12T14:18:22.188944+00:00",
"end": "2026-09-12T14:18:51.165208+00:00",
"command": [
"rustc",
"--edition=2024",
"-D",
"warnings",
"-C",
"linker=clang.exe",
"-L",
"dependency=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps",
"--extern",
"serde_json=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"--extern",
"oa_core=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\nested-call-bound-fixture-20260912.rs",
"-o",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-bound-r1-b250-core\\nested-bound-r1-b250-core.exe"
],
"exit": 0,
"files": [
{
"path": ".local\\nested-call-bound-fixture-20260912.rs",
"bytes": 15775,
"sha256": "d74a482d6433a8e0d8e82ae67eaca1b28df92920466bbb247ffac0bd1cd80928"
},
{
"path": "target\\debug\\.fingerprint\\oa-desktop-e358a5ed59b1de41\\test-bin-arcadia-studio.json",
"bytes": 1079,
"sha256": "cbf2edff63ac3601b264065345b7c77e456ed8e34b09ebd204d5785d2324a2dd"
},
{
"path": "target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"bytes": 2991440,
"sha256": "485300d4125a69de7da5413f832eb4b9cde547e60f103f3b284e49aee4194d05"
},
{
"path": "target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"bytes": 102820446,
"sha256": "d395dbe1f761d41bce3cb850f44cdc0343b0db172bd03abf89e455c44f4b7e86"
},
{
"path": ".local\\native-qa\\nested-bound-r1-b250-core\\nested-bound-r1-b250-core.exe",
"bytes": 18501120,
"sha256": "6d004f8a86bc3e73ef961c4fad2209bf7979823761c40f43d376879a024b77a6"
}
]
}
@@ -0,0 +1,9 @@
{
"utc": "2026-09-12T14:19:47.716690+00:00",
"command": [
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-bound-r1-b250-core\\nested-bound-r1-b250-core.exe",
".local/native-qa/bound-nested-fixture-r1-20260912"
],
"binary_sha256": "6d004f8a86bc3e73ef961c4fad2209bf7979823761c40f43d376879a024b77a6",
"exit": 0
}
@@ -0,0 +1 @@
PASS: 34 elements, 6 relations, three bound scenarios, zero Errors, 2 warnings; whole-Project roundtrip and exact SA operation resolution
@@ -0,0 +1,52 @@
{
"source": "42511f0f8ae46f29e11ae18c1030face654c2994",
"current_head": "f7d84faf30cdb5e3695c2dc16ee0a5f2eac91e71",
"scope": "Core-only standalone probe; core/configuration/lock inputs match identified full test run. Desktop changes do not become core evidence.",
"start": "2026-09-12T13:27:52.562653+00:00",
"end": "2026-09-12T13:28:02.627505+00:00",
"command": [
"rustc",
"--edition=2024",
"-D",
"warnings",
"-C",
"linker=clang.exe",
"-L",
"dependency=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps",
"--extern",
"serde_json=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"--extern",
"oa_core=C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\nested-call-fixtures-20260912.rs",
"-o",
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-call-f7-core\\nested-call-f7-core.exe"
],
"exit": 0,
"files": [
{
"path": ".local\\nested-call-fixtures-20260912.rs",
"bytes": 6789,
"sha256": "e296ad928a4e01321d20cbcfb046a64a733561cf6f7a07d8d52eaeb64d4e4ee2"
},
{
"path": "target\\debug\\.fingerprint\\oa-desktop-e358a5ed59b1de41\\test-bin-arcadia-studio.json",
"bytes": 1079,
"sha256": "cbf2edff63ac3601b264065345b7c77e456ed8e34b09ebd204d5785d2324a2dd"
},
{
"path": "target\\debug\\deps\\libserde_json-da2c4f333fc47818.rlib",
"bytes": 2991440,
"sha256": "485300d4125a69de7da5413f832eb4b9cde547e60f103f3b284e49aee4194d05"
},
{
"path": "target\\debug\\deps\\liboa_core-145180297aa692e4.rlib",
"bytes": 102820446,
"sha256": "d395dbe1f761d41bce3cb850f44cdc0343b0db172bd03abf89e455c44f4b7e86"
},
{
"path": ".local\\native-qa\\nested-call-f7-core\\nested-call-f7-core.exe",
"bytes": 18152448,
"sha256": "1beea08d2b9b0892d0ecd9892b77466f57e290163b5684d4e3f4c883b53d9b26"
}
]
}
@@ -0,0 +1,9 @@
{
"utc": "2026-09-12T13:28:32.861887+00:00",
"command": [
"C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\native-qa\\nested-call-f7-core\\nested-call-f7-core.exe",
".local/native-qa/nested-call-fixture-20260912"
],
"binary_sha256": "1beea08d2b9b0892d0ecd9892b77466f57e290163b5684d4e3f4c883b53d9b26",
"exit": 0
}
@@ -0,0 +1 @@
PASS actual core validation and canonical round trip: 132 elements, 77 warnings; no desktop draw/hit/runtime claim
@@ -0,0 +1,841 @@
[
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000000001,
),
code: "missing_system",
field: Some(
"elements",
),
message: "Identify the SA system of interest.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002710,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Caller work' to a performer.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002711,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Callee work' to a performer.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002792,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000279c,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000027a6,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000027b0,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000027f6,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002800,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000280a,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002814,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000285a,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002864,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000286e,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002878,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000028be,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000028c8,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000028d2,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000028dc,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002922,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000292c,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002936,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002940,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002986,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002990,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029a4,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029e0,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029e1,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029e3,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029e4,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000029f4,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a12,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a44,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a45,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a47,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a48,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002a58,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004e20,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Caller work' to a performer.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004e21,
),
code: "unallocated_leaf",
field: Some(
"allocation",
),
message: "Allocate leaf 'Callee work' to a performer.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004ea2,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004eac,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004eb6,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004ec0,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f06,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f10,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f1a,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f24,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f6a,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f74,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f7e,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004f88,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004fce,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004fd8,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004fe2,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004fec,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005032,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-00000000503c,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005046,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005050,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005096,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050a0,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050b4,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050f0,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050f1,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050f3,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-0000000050f4,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005104,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005122,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005154,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005155,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005157,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005158,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000005168,
),
code: "missing_exchange",
field: Some(
"exchange",
),
message: "Choose an exchange.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002710,
),
code: "work_without_behavior_context",
field: None,
message: "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000002711,
),
code: "work_without_behavior_context",
field: None,
message: "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004e20,
),
code: "work_without_behavior_context",
field: None,
message: "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
},
Issue {
severity: Warning,
element: Some(
21ca1100-0000-4000-8000-000000004e21,
),
code: "work_without_behavior_context",
field: None,
message: "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
},
]
@@ -0,0 +1,140 @@
# Canvas focus continuity after accepted edits and history
Native parent observation at immutable `f7d84fa`: allocation work canvas click,
Delete placement, Save, Undo, Save, Redo, Save, Undo, then Shift+Delete produces
no dialog. Clicking the same work restores the expected model confirmation.
Parent evidence: `.local/native-qa/delete-20260912/evidence/03-shift-delete-after-undo-no-dialog.png`
and `04modelconfirmafterclick`.
Isolated worktree: `C:/Users/User/Documents/Git/ARCADIA-Studio/.local/delete-undo-focus-20260912`.
Branch: `codex/delete-undo-focus-956atf-20260912`, baseline `f7d84fa`.
Reviewed change commit: `7bf57db` (six production/test files; no runtime gate run
by this worker).
## Source diagnosis and change
`diagram::focus::Owner::diff` previously revoked keyboard focus whenever any
`route_edit::Context` field changed. The context includes document revision,
so accepting a placement removal or Undo/Redo revoked the focus that still
belonged to the displayed canvas. This is a production defect, unlike the
earlier retained-Apply evaluator ordering issue.
Owner now preserves its existing focused and traversing values across a revision
change only. It clears pointer history for every changed full context. It still
revokes focus on document, diagram or canvas epoch change, disabled input, window
loss/resize, a pointer press outside the canvas, and actual focus operations.
It never grants focus on a revision change. The inner canvas still receives the
new full context, and model/confirmation intent validation is unchanged.
A second necessary cause is passive Save: `save_to` sets `busy`, while the old
single enabled flag treated busy like a modal owner transfer. The shared keyboard
predicate now distinguishes logical owner availability from busy command
admission, with the previous command predicate behavior unchanged. Architecture
and state callers pass both owner availability and busy suspension to Owner.
Save/export suspension retains existing focus but denies keyboard/pointer
commands and focus acquisition. Explicit unfocus operations, outside clicks and
window loss still revoke the owner. No App save logic was changed.
Entering suspension recreates only the wrapped canvas child tree once, retiring
transient drags that could otherwise miss a release during I/O. Outer keyboard
focus and App-owned diagram presentation remain intact.
`refresh_draft` restores the selected architecture/state placement from the
preserved semantic selection after Undo; no selection-history rewrite is needed.
## Actual mounted regression changes
`app/diagram_removal_tests.rs` adds:
- OA and SA repeated placement Delete after Undo/Redo, then Shift+Delete without
another pointer event or explicit focus operation; exact bundle comparisons.
- Native allocation work click/hide/Undo/Redo/Undo/Shift+Delete sequence with the
expected work confirmation target and unchanged model/source bundle. Real
saves are included between history steps; each task is held while the actual
busy tree receives F2/Shift+Delete, then completed to a temporary source file.
- Persisted State placement hide/Undo/Shift+Delete, preserving the architecture
placement and correctly targeting the state.
- A canvas-versus-Inspector test that starts with actual pointer focus, accepts
an unrelated rename, traverses Undo/Redo/Undo, checks the real widget operation
focus witness, and verifies F2 reaches only the canvas. Repeated F2/Delete
cannot reach the diagram from either Inspector or the newly opened inline name.
- Actual Inspector click or programmatic field focus during a held Save must
revoke the retained canvas owner, including after the Save task completes.
- Passive Save cannot grant focus through either a focus operation or pointer
press, and cannot restore focus lost to a window Unfocused event while busy.
- An actual held diagram drag is released while Save is busy; later motion and
release must not commit that old gesture. A fresh drag must work and Undo must
restore the exact original source bundle.
Existing stale revision/epoch/document/selection intent, confirmation replay,
modal/window/dirty/modified-key, clean Inspector and Tab tests remain unchanged.
## Derived viewport assessment
The process/scenario Viewport has analogous revision revocation, but its Scope
epoch is `camera_epoch`, mixing revisions and keyboard ownership boundaries.
Passing `canvas_epoch` as a replacement focus lifetime is unsafe: process/scenario
ordinary pan, zoom, viewport sizing and scroll changes also increment that epoch.
A separate derived focus-lifetime contract needs a bounded follow-up; this patch
does not broaden to those views or weaken their current guards.
## Verification boundary
`git diff --check` passes. This worker has run no Cargo/compiler or native GUI
commands and made no Gitea changes. Parent owns executable gates and native
recheck. Fresh Astra/high source review found no issue in the revision-only
change, then identified a high-priority focus-theft bug in the first busy draft:
omitting the suspended canvas from focus operations and outside clicks prevented
Inspector acquisition from revoking retained canvas focus. The current change
keeps unfocus and outside-click revocation active, denies only acquisition, and
adds both actual click and operation regressions.
The same critic then identified the swallowed-release gesture risk described
above; the child-tree retirement and actual held/fresh drag regression address
it. The final Astra/high source re-review reports no further findings and judges
the actual gesture regression meaningful. No executable pass is claimed.
Recommended existing root Windows gate filters:
- `app::diagram_removal_tests::` (all affected actual-root tests)
- `app::architecture_authoring::tests::` (inline F2 and ownership dependents)
The new test is
`app::diagram_removal_tests::mounted_revision_and_history_preserve_the_actual_canvas_or_inspector_focus_owner`.
The original allocation filter remains
`app::diagram_removal_tests::mounted_allocation_work_click_then_delete_without_redraw_removes_placement_once`.
## Persisted-state structural correction after root gate
Root's Windows gate at integrated `24bb24839b14353c1a2b668f3bac34235d39f7a7`
reported 22 pass / 1 fail with all 531 inputs unchanged. Remaining failure:
`mounted_state_delete_uses_state_placement_and_preserves_architecture_placement`
had no delete_target after its real Save/Undo/Shift+Delete sequence. Other new
revision, passive-save, Inspector and gesture tests passed in that scoped gate.
Receipt: root `.local/allocation-union-20260912/focus-24bb248.result.json`.
Source cause is separate from Context focus retention: state_view conditionally
pushed the unplaced-element picker immediately before the canvas Owner. The
installed `iced_widget-0.14.2/src/column.rs:207` delegates to Tree::diff_children.
Installed `iced_core-0.14.0/src/widget/tree.rs` reconciles children positionally
with zip, appending or truncating, and replaces a Tree on widget-tag mismatch.
When Hide adds the picker, the former Owner slot becomes a PickList and a new
unfocused Owner is appended. Undo truncates that Owner and replaces PickList
with yet another new Owner. StateCanvas has no private focused flag; state
viewport resizing does not change canvas_epoch. Loosening Context guards would
therefore not fix the cause.
The corrected view has a permanent body Column with exactly two children:
an optional-controls Column, and the canvas Owner. Picker insertion/removal is
confined to the first child's subtree. The body uses zero spacing when that
slot is empty and eight pixels when present, preserving the previous visible
layout without a blank spacer. No new focus is assigned.
The original failing test now checks the unplaced set's empty/one/empty
transition, unchanged document/diagram/epoch ownership after Hide, and actual
mounted focus immediately after Hide, Save and Undo. Redraws after Hide/Undo
exercise the real viewport transition. The final Shift+Delete target assertion
and source preservation remain unchanged. These additional checks are unrun;
root owns the next gate. Source review was requested from root because the
independent critic tool currently rejects follow-up with a thread-limit error.
Root completed source review and authorized the scoped correction, committed
as `e24054b` on the original isolated branch. Runtime verification remains with
root; no compiler/native action was performed by this worker.
@@ -183,5 +183,35 @@
"file": "gui-f7d84fa.result.json",
"bytes": 351,
"sha256": "3e8f2dbcc46b3ed78855c3139c9c721313acaa0c94deafc5ce91b49c66a64f0a"
},
{
"file": "focus-24bb248.log",
"bytes": 3587,
"sha256": "7e2f754096cf9a419f6b819fe251f2509b67a4930cd46327c750578d24357996"
},
{
"file": "focus-24bb248.inputs.json",
"bytes": 98922,
"sha256": "53c3c1cb5b0766324ad9338b8101f7317610c182916e18d1debda20b8c8af08d"
},
{
"file": "focus-24bb248.result.json",
"bytes": 408,
"sha256": "8bd63a97a9557f2d26825362a01580a3604257268911e6b9244de98b8eb5e062"
},
{
"file": "clippy-e2c027e.log",
"bytes": 1076,
"sha256": "e73f770457d5a2e56c8f41e637700cb77f539e6a326e0df51cc244876bc16c1e"
},
{
"file": "clippy-e2c027e.inputs.json",
"bytes": 99306,
"sha256": "341ff6023fc288c36011515a68ad5743d31e3b8506d0d03d64a382392198ff94"
},
{
"file": "clippy-e2c027e.result.json",
"bytes": 379,
"sha256": "296c4f96f1e1d08b4d447e03edce442a53d830101f867876fc1edd20882dccf9"
}
]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
Checking oa-desktop v0.2.0 (C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-desktop)
error: variant `Focus` is never constructed
--> crates\oa-desktop\src\behavior\scenario.rs:133:5
|
126 | pub enum CanvasEvent {
| ----------- variant in this enum
...
133 | Focus(Context),
| ^^^^^
|
= note: `CanvasEvent` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
= note: `-D dead-code` implied by `-D warnings`
= help: to override `-D warnings` add `#[expect(dead_code)]` or `#[allow(dead_code)]`
error: variant `Focus` is never constructed
--> crates\oa-desktop\src\behavior\topology\direct.rs:65:5
|
29 | pub enum CanvasEvent {
| ----------- variant in this enum
...
65 | Focus(Context),
| ^^^^^
|
= note: `CanvasEvent` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
error: could not compile `oa-desktop` (bin "arcadia-studio") due to 2 previous errors
@@ -0,0 +1,19 @@
{
"source": "e2c027e54c6be0d051820374f669c52278d90f97",
"start": "2026-09-12T14:20:12.264374+00:00",
"end": "2026-09-12T21:53:39.024944+00:00",
"exit": 101,
"command": [
"cargo",
"clippy",
"--locked",
"-p",
"oa-desktop",
"--all-targets",
"--",
"-D",
"warnings"
],
"inputs": "clippy-e2c027e.inputs.json",
"changed_inputs": []
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,44 @@
Compiling oa-desktop v0.2.0 (C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-desktop)
Finished `test` profile [unoptimized] target(s) in 17m 48s
Running unittests src\main.rs (target\debug\deps\arcadia_studio-e358a5ed59b1de41.exe)
running 23 tests
test app::diagram_focus_tests::mounted_f2_requires_canvas_focus_and_yields_to_clean_inspector ... ok
test app::diagram_removal_tests::captured_confirmation_rejects_selection_document_revision_and_placement_changes ... ok
test app::diagram_removal_tests::mounted_allocation_work_click_then_delete_without_redraw_removes_placement_once ... ok
test app::diagram_removal_tests::mounted_bend_delete_has_priority_and_semantic_edge_delete_is_confirmed ... ok
test app::diagram_removal_tests::mounted_clean_text_programmatic_focus_and_tab_remove_canvas_keyboard_ownership ... ok
test app::diagram_removal_tests::mounted_inspector_can_take_focus_during_passive_save ... ok
test app::diagram_removal_tests::mounted_oa_sa_delete_hides_exact_placement_and_round_trips_source_and_model ... ok
test app::diagram_removal_tests::mounted_passive_save_never_grants_canvas_focus_or_restores_window_lost_focus ... ok
test app::diagram_removal_tests::mounted_passive_save_retires_held_canvas_drag_but_allows_a_fresh_drag ... ok
test app::diagram_removal_tests::mounted_placement_properties_selection_supports_hide_and_model_delete ... ok
test app::diagram_removal_tests::mounted_programmatic_canvas_entry_needs_no_mouse_focus_boolean ... ok
test app::diagram_removal_tests::mounted_removal_intents_reject_stale_selection_revision_owner_and_document ... ok
test app::diagram_removal_tests::mounted_revision_and_history_preserve_the_actual_canvas_or_inspector_focus_owner ... ok
test app::diagram_removal_tests::mounted_shift_delete_captures_target_cancel_escape_and_confirmation_replay ... ok
test app::diagram_removal_tests::mounted_state_action_and_internal_rows_delete_relations_and_preserve_reused_work ... ok
test app::diagram_removal_tests::mounted_state_delete_uses_state_placement_and_preserves_architecture_placement ... FAILED
test app::diagram_removal_tests::mounted_window_modal_dirty_and_modifier_boundaries_retire_delete_focus ... ok
test app::diagram_selection_tests::changing_visible_view_follows_its_clean_inspector_without_a_model_edit ... ok
test app::diagram_selection_tests::diagram_switch_preserves_an_independent_semantic_inspection ... ok
test app::diagram_selection_tests::diagram_uuid_edit_follows_the_same_view_or_a_different_picker_target ... ok
test app::diagram_selection_tests::invalid_view_draft_blocks_the_switch_and_retains_raw_input ... ok
test app::diagram_selection_tests::stale_diagram_choice_never_applies_an_unrelated_valid_draft ... ok
test app::diagram_selection_tests::valid_pending_view_edit_applies_to_old_view_then_inspects_the_new_view ... ok
failures:
---- app::diagram_removal_tests::mounted_state_delete_uses_state_placement_and_preserves_architecture_placement stdout ----
thread 'app::diagram_removal_tests::mounted_state_delete_uses_state_placement_and_preserves_architecture_placement' (6984) panicked at crates\oa-desktop\src\app\diagram_removal_tests.rs:1065:36:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
app::diagram_removal_tests::mounted_state_delete_uses_state_placement_and_preserves_architecture_placement
test result: FAILED. 22 passed; 1 failed; 0 ignored; 0 measured; 799 filtered out; finished in 271.47s
error: test failed, to rerun pass `-p oa-desktop --bin arcadia-studio`
@@ -0,0 +1,20 @@
{
"source": "24bb24839b14353c1a2b668f3bac34235d39f7a7",
"start": "2026-09-12T13:41:39.248143+00:00",
"end": "2026-09-12T14:04:09.176393+00:00",
"exit": 101,
"command": [
"cargo",
"test",
"--locked",
"-p",
"oa-desktop",
"--bin",
"arcadia-studio",
"app::diagram",
"--",
"--test-threads=1"
],
"inputs": "focus-24bb248.inputs.json",
"changed_inputs": []
}