Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ef6868f00 | ||
|
|
819c360a6c | ||
|
|
1e848019d9 | ||
|
|
be00bc6f65 | ||
|
|
32334c6631 | ||
|
|
09da0d90aa | ||
|
|
865483b4c6 | ||
|
|
f796a27277 | ||
|
|
321897fd55 | ||
|
|
4da92ab481 | ||
|
|
7831abeed5 | ||
|
|
fe8364e7eb | ||
|
|
3ade397008 | ||
|
|
29084050a7 | ||
|
|
cb611a71df | ||
|
|
fb77b857d2 | ||
|
|
a9a5768144 | ||
|
|
d041212d17 | ||
|
|
b96286ea37 | ||
|
|
dd42d0ab01 | ||
|
|
023e3a8f09 | ||
|
|
52f6e176cd | ||
|
|
b853a1eca3 | ||
|
|
e207b04114 | ||
|
|
c81e285573 | ||
|
|
c6d296a7e2 | ||
|
|
80cf197be6 | ||
|
|
865cd55e21 | ||
|
|
6d2b9619e3 | ||
|
|
65a1fef17c | ||
|
|
ef1a4478c5 | ||
|
|
632c8feb36 | ||
|
|
aa40345281 | ||
|
|
8547b85825 | ||
|
|
4ecad78433 | ||
|
|
ff615d0ee0 | ||
|
|
eb5161b207 | ||
|
|
aed40d7464 |
@@ -15,33 +15,49 @@
|
|||||||
|
|
||||||
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
|
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
|
||||||
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
|
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
|
||||||
|
PROJECT_GOAL.md -text whitespace=cr-at-eol
|
||||||
harness/fixtures/release1-native/** -text
|
harness/fixtures/release1-native/** -text
|
||||||
harness/fixtures/interface-operation-probe/** -text
|
harness/fixtures/interface-operation-probe/** -text
|
||||||
harness/history/2026-09-06-ledgers-before-consolidation/** -text
|
harness/history/2026-09-06-ledgers-before-consolidation/** -text
|
||||||
|
harness/history/2026-09-10-ledgers-before-continuation-refresh/** -text
|
||||||
harness/history/2026-09-10-ledgers-before-second-consolidation/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
harness/history/2026-09-10-ledgers-before-second-consolidation/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||||
harness/experiments/textual-authoring-20260911-local/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
harness/experiments/textual-authoring-20260911-local/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||||
harness/experiments/constructor-defaults-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
harness/history/2026-09-11-ytr83r-integration/** -text whitespace=cr-at-eol
|
||||||
harness/experiments/constructor-defaults-20260911/results/*.arcadia whitespace=-blank-at-eol,cr-at-eol
|
|
||||||
harness/experiments/behavior-creation-native-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
|
||||||
harness/experiments/analysis-context-20260911/** -text whitespace=cr-at-eol
|
|
||||||
harness/experiments/textual-authoring-20260911/** -text whitespace=cr-at-eol
|
|
||||||
harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia whitespace=-blank-at-eol,cr-at-eol
|
|
||||||
harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
|
|
||||||
crates/oa-desktop/src/recovery/fixtures/*.json -text
|
crates/oa-desktop/src/recovery/fixtures/*.json -text
|
||||||
harness/fixtures/source7-native/** -text
|
harness/fixtures/source7-native/** -text
|
||||||
harness/fixtures/basic-editing/** -text
|
harness/fixtures/basic-editing/** -text
|
||||||
|
harness/fixtures/scenario-authoring/base/** -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/textual-authoring-20260911/** -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/textual-authoring-20260911/observed/split-entry.arcadia whitespace=-blank-at-eol,cr-at-eol
|
||||||
|
harness/experiments/analysis-context-20260911/** -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/constructor-defaults-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||||
|
harness/experiments/constructor-defaults-20260911/results/*.arcadia whitespace=-blank-at-eol,cr-at-eol
|
||||||
|
harness/experiments/behavior-creation-native-20260911/** -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
|
||||||
harness/behavior-creation-contract.rs -text whitespace=cr-at-eol
|
harness/behavior-creation-contract.rs -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/source-cli-independent-20260911/** -text whitespace=cr-at-eol
|
||||||
|
harness/fixtures/scenario-lifecycle-native/**/*.arcadia -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/lifecycle-native-ytr83r-20260911/** -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/lifecycle-native-ytr83r-20260911/*.log -text -whitespace
|
||||||
|
harness/experiments/lifecycle-native-ui-oa-20260911/** -text -whitespace
|
||||||
|
harness/experiments/lifecycle-native-ui-sa-20260911/** -text -whitespace
|
||||||
|
harness/experiments/wsl-postboot-ytr83r-20260911/** -text -whitespace
|
||||||
|
|
||||||
# Preserve checksum-bound font notices verbatim through Git round trips.
|
# Preserve checksum-bound font notices verbatim through Git round trips.
|
||||||
scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING -text
|
scripts/windows-notices/GNOME-Cantarell-61362600e104-COPYING -text
|
||||||
scripts/windows-notices/Fira-OFL.txt -text
|
scripts/windows-notices/Fira-OFL.txt -text
|
||||||
scripts/windows-notices/FiraSans-Regular-NOTICE.txt -text
|
scripts/windows-notices/FiraSans-Regular-NOTICE.txt -text
|
||||||
|
scripts/windows-notices/llvm-6dfe1677ab8d-libunwind-LICENSE.TXT -text
|
||||||
|
|
||||||
# Preserve exact inputs of the frozen D4D9 authoring milestone build.
|
harness/experiments/lifecycle-header-overflow-before-20260911/** -text -whitespace
|
||||||
crates/oa-desktop/src/semantic_review/artifacts_view.rs -text
|
harness/history/2026-09-11-ytr83r-record-migration/STATE-before.md -text -whitespace
|
||||||
crates/oa-desktop/src/semantic_review/attachments_view.rs -text
|
|
||||||
crates/oa-desktop/src/semantic_review/keyed_rows.rs -text
|
harness/fixtures/staged-source-authoring/** -text whitespace=cr-at-eol
|
||||||
crates/oa-desktop/src/semantic_review/keyed_tests.rs -text
|
harness/staged-source-authoring-contract.rs -text whitespace=cr-at-eol
|
||||||
crates/oa-desktop/src/semantic_review/keyed.rs -text
|
|
||||||
crates/oa-desktop/src/semantic_review/ordered.rs -text
|
harness/experiments/contextual-lifecycle-integration-ytr83r-20260911/** -text -whitespace
|
||||||
docs/openapi.json -text
|
harness/experiments/windows-runtime-321897f-20260911/** -text -whitespace
|
||||||
|
harness/child-work-native-contract.rs -text whitespace=cr-at-eol
|
||||||
|
harness/fixtures/child-work-native/** -text whitespace=cr-at-eol
|
||||||
|
harness/experiments/windows-gates-865483b-20260911/** -text -whitespace
|
||||||
|
harness/experiments/native-child-oa-865483b-20260911/** -text -whitespace
|
||||||
|
harness/experiments/packaged-server-865483b-20260911/** -text -whitespace
|
||||||
|
|||||||
@@ -2139,6 +2139,15 @@ dependencies = [
|
|||||||
"syn 2.0.119",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oa-cli"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"oa-core",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oa-core"
|
name = "oa-core"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/oa-core", "crates/oa-desktop", "crates/oa-server"]
|
members = ["crates/oa-core", "crates/oa-desktop", "crates/oa-server", "crates/oa-cli"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ From the repository root:
|
|||||||
cargo run --locked -p oa-desktop --bin arcadia-studio
|
cargo run --locked -p oa-desktop --bin arcadia-studio
|
||||||
```
|
```
|
||||||
|
|
||||||
Build both desktop and server release executables:
|
Build the desktop, server and source CLI release executables:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo build --release --workspace --locked
|
cargo build --release --workspace --locked
|
||||||
@@ -51,6 +51,21 @@ The earlier V2 package record describes `dist/ArcadiaStudio-Windows.zip` and `di
|
|||||||
|
|
||||||
The current [Windows packaging instructions](docs/windows-packaging.md) stage both executables, verify their runtime DLL dependencies, and produce an archive with checksums and provenance. Native acceptance fixtures and independent expected results are retained in [the release fixture directory](harness/fixtures/release1-native/README.md).
|
The current [Windows packaging instructions](docs/windows-packaging.md) stage both executables, verify their runtime DLL dependencies, and produce an archive with checksums and provenance. Native acceptance fixtures and independent expected results are retained in [the release fixture directory](harness/fixtures/release1-native/README.md).
|
||||||
|
|
||||||
|
## Work with source files from the command line
|
||||||
|
|
||||||
|
The `arcadia` CLI checks an explicit `.arcadia` entry file and its includes without
|
||||||
|
starting the desktop or server:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run --locked -p oa-cli --bin arcadia -- check examples/text-first/project.arcadia
|
||||||
|
```
|
||||||
|
|
||||||
|
`stabilize ROOT` previews the identities needed for persistent editing;
|
||||||
|
`rename-alias ROOT OLD NEW` previews an alias change and its references. Add
|
||||||
|
`--write` to save that invocation's candidate, or `--json` for structured
|
||||||
|
diagnostics and changed-file text/diffs. Preview leaves files untouched.
|
||||||
|
See [source CLI commands](docs/source-cli.md) for examples and exit codes.
|
||||||
|
|
||||||
## Model operational needs and system responsibilities
|
## Model operational needs and system responsibilities
|
||||||
|
|
||||||
The desktop starts at **Home**, where you can create a model, open a local project, continue recent files or recover retained work. Missing recent files stay visible with **Locate**; **Remove** only forgets their history entry. Choose **Explore rescue example** for a model combining people, an organization, a System of Interest, information and physical exchange items, normal/degraded behavior and code/CAD evidence.
|
The desktop starts at **Home**, where you can create a model, open a local project, continue recent files or recover retained work. Missing recent files stay visible with **Locate**; **Remove** only forgets their history entry. Choose **Explore rescue example** for a model combining people, an organization, a System of Interest, information and physical exchange items, normal/degraded behavior and code/CAD evidence.
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "oa-cli"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "arcadia"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
oa-core = { path = "../oa-core" }
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
@@ -0,0 +1,451 @@
|
|||||||
|
//! Source-only authoring. Preparation captures bytes; only core owns disk writes.
|
||||||
|
use oa_core::{
|
||||||
|
Command, language,
|
||||||
|
persistence::MigrationReport,
|
||||||
|
prepare_command,
|
||||||
|
source_files::{self, DiskProject, SourceFileError},
|
||||||
|
source_project::{self, CompiledSourceProject, ProjectLimits, SourceDiagnostic},
|
||||||
|
validation::Severity,
|
||||||
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub const LIMITS: ProjectLimits = ProjectLimits {
|
||||||
|
max_files: 256,
|
||||||
|
max_include_depth: 32,
|
||||||
|
max_include_edges: 1024,
|
||||||
|
model: language::ParseLimits {
|
||||||
|
max_source_bytes: 64 * 1024 * 1024,
|
||||||
|
max_graph_objects: 100_000,
|
||||||
|
max_records: 250_000,
|
||||||
|
max_step_references: 500_000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Operation {
|
||||||
|
Check,
|
||||||
|
Stabilize,
|
||||||
|
RenameAlias { old: String, new: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Location {
|
||||||
|
pub path: String,
|
||||||
|
/// Half-open UTF-8 byte offsets. Line/column are one-based Unicode scalars.
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub line: Option<usize>,
|
||||||
|
pub column: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Diagnostic {
|
||||||
|
pub severity: String,
|
||||||
|
pub code: String,
|
||||||
|
pub message: String,
|
||||||
|
pub coordinate_space: String,
|
||||||
|
pub location: Option<Location>,
|
||||||
|
pub related: Vec<Location>,
|
||||||
|
pub object_id: Option<String>,
|
||||||
|
pub field: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Change {
|
||||||
|
pub path: String,
|
||||||
|
/// Exact captured and proposed UTF-8 text, including line endings.
|
||||||
|
pub before: String,
|
||||||
|
pub after: String,
|
||||||
|
pub diff: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct Report {
|
||||||
|
pub format_version: u32,
|
||||||
|
pub entry: String,
|
||||||
|
pub operation: String,
|
||||||
|
pub status: String,
|
||||||
|
pub exit_code: u8,
|
||||||
|
/// True only after a successful changed save. False on failure does not
|
||||||
|
/// promise rollback: consult the specific error/recovery outcome.
|
||||||
|
pub written: bool,
|
||||||
|
pub diagnostics: Vec<Diagnostic>,
|
||||||
|
pub changes: Vec<Change>,
|
||||||
|
pub identities_to_stabilize: usize,
|
||||||
|
pub migration: Option<MigrationReport>,
|
||||||
|
pub notes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
pub fn failure(
|
||||||
|
entry: &Path,
|
||||||
|
operation: &str,
|
||||||
|
exit_code: u8,
|
||||||
|
code: &str,
|
||||||
|
message: String,
|
||||||
|
) -> Self {
|
||||||
|
let mut report = Self::empty(entry, operation);
|
||||||
|
report.fail(exit_code, code, message);
|
||||||
|
report
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty(entry: &Path, operation: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
format_version: 1,
|
||||||
|
entry: entry.to_string_lossy().into_owned(),
|
||||||
|
operation: operation.into(),
|
||||||
|
status: "checked".into(),
|
||||||
|
exit_code: 0,
|
||||||
|
written: false,
|
||||||
|
diagnostics: vec![],
|
||||||
|
changes: vec![],
|
||||||
|
identities_to_stabilize: 0,
|
||||||
|
migration: None,
|
||||||
|
notes: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail(&mut self, exit_code: u8, code: &str, message: String) {
|
||||||
|
self.status = "rejected".into();
|
||||||
|
self.exit_code = exit_code;
|
||||||
|
self.diagnostics.push(Diagnostic {
|
||||||
|
severity: "error".into(),
|
||||||
|
code: code.into(),
|
||||||
|
message,
|
||||||
|
coordinate_space: "operation".into(),
|
||||||
|
location: None,
|
||||||
|
related: vec![],
|
||||||
|
object_id: None,
|
||||||
|
field: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn located(issue: SourceDiagnostic, space: &str) -> Diagnostic {
|
||||||
|
let d = issue.diagnostic;
|
||||||
|
Diagnostic {
|
||||||
|
severity: match d.severity {
|
||||||
|
Severity::Error => "error",
|
||||||
|
Severity::Warning => "warning",
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
code: d.code,
|
||||||
|
message: d.message,
|
||||||
|
coordinate_space: space.into(),
|
||||||
|
location: Some(Location {
|
||||||
|
path: issue.path,
|
||||||
|
start: d.span.start,
|
||||||
|
end: d.span.end,
|
||||||
|
line: Some(d.line),
|
||||||
|
column: Some(d.column),
|
||||||
|
}),
|
||||||
|
related: issue
|
||||||
|
.related
|
||||||
|
.into_iter()
|
||||||
|
.map(|at| Location {
|
||||||
|
path: at.path,
|
||||||
|
start: at.span.start,
|
||||||
|
end: at.span.end,
|
||||||
|
line: None,
|
||||||
|
column: None,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
object_id: d.object_id.map(|id| id.to_string()),
|
||||||
|
field: d.field,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disk_error(report: &mut Report, error: SourceFileError) {
|
||||||
|
let code = match &error {
|
||||||
|
SourceFileError::Conflict(_) => "external_change",
|
||||||
|
SourceFileError::RecoveryRequired(_) => "recovery_required",
|
||||||
|
SourceFileError::RecoveryFailed { .. } => "recovery_failed",
|
||||||
|
SourceFileError::Path(_) => "unsafe_path",
|
||||||
|
SourceFileError::Limit(_) => "source_limit",
|
||||||
|
SourceFileError::UnknownExpectation(_) => "unknown_disk_expectation",
|
||||||
|
SourceFileError::Invalid(_) => "invalid_save_candidate",
|
||||||
|
SourceFileError::Io { .. } => "io_error",
|
||||||
|
};
|
||||||
|
report.fail(3, code, error.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A candidate owns its original expectations and is consumed by saving.
|
||||||
|
/// Callers cannot replace its source/model/expectations after preparation.
|
||||||
|
pub struct Prepared {
|
||||||
|
report: Report,
|
||||||
|
snapshot: DiskProject,
|
||||||
|
compiled: CompiledSourceProject,
|
||||||
|
mutable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prepared {
|
||||||
|
pub fn report(&self) -> &Report {
|
||||||
|
&self.report
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_report(self) -> Report {
|
||||||
|
self.report
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(mut self) -> Report {
|
||||||
|
if !self.mutable {
|
||||||
|
self.report
|
||||||
|
.fail(2, "usage", "check/lint does not accept --write".into());
|
||||||
|
return self.report;
|
||||||
|
}
|
||||||
|
match source_files::save_project(&self.snapshot, &self.compiled, LIMITS) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.report.written = !self.report.changes.is_empty();
|
||||||
|
self.report.status = if self.report.written {
|
||||||
|
"written"
|
||||||
|
} else {
|
||||||
|
"unchanged"
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
Err(error) => disk_error(&mut self.report, error),
|
||||||
|
}
|
||||||
|
self.report
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare(entry: &Path, operation: Operation) -> Result<Prepared, Box<Report>> {
|
||||||
|
let name = match operation {
|
||||||
|
Operation::Check => "check",
|
||||||
|
Operation::Stabilize => "stabilize",
|
||||||
|
Operation::RenameAlias { .. } => "rename-alias",
|
||||||
|
};
|
||||||
|
let mut report = Report::empty(entry, name);
|
||||||
|
let snapshot = source_files::load_project(entry, LIMITS).map_err(|error| {
|
||||||
|
disk_error(&mut report, error);
|
||||||
|
Box::new(report.clone())
|
||||||
|
})?;
|
||||||
|
let raw = snapshot.reachable_bundle();
|
||||||
|
// The loader may include warnings from stabilized compilation. Only its
|
||||||
|
// discovery/IO diagnostics are authoritative on the original bytes.
|
||||||
|
let mut diagnostics: Vec<_> = snapshot
|
||||||
|
.diagnostics
|
||||||
|
.iter()
|
||||||
|
.filter(|d| {
|
||||||
|
matches!(
|
||||||
|
d.diagnostic.code.as_str(),
|
||||||
|
"pending_save"
|
||||||
|
| "source_encoding"
|
||||||
|
| "source_limit"
|
||||||
|
| "invalid_path"
|
||||||
|
| "missing_include"
|
||||||
|
| "project_limit"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
for diagnostic in source_project::lint_bundle(&raw, LIMITS) {
|
||||||
|
if !diagnostics.contains(&diagnostic) {
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let disk_refusal = snapshot.diagnostics.iter().any(|d| {
|
||||||
|
matches!(
|
||||||
|
d.diagnostic.code.as_str(),
|
||||||
|
"pending_save" | "missing_include" | "invalid_path"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let invalid = diagnostics
|
||||||
|
.iter()
|
||||||
|
.any(|d| d.diagnostic.severity == Severity::Error);
|
||||||
|
report.diagnostics = diagnostics
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| located(d, "input"))
|
||||||
|
.collect();
|
||||||
|
if invalid {
|
||||||
|
// Do not run declaration analysis or any updater on invalid raw text.
|
||||||
|
// Failed lint may also have allocated provisional object identities.
|
||||||
|
for diagnostic in &mut report.diagnostics {
|
||||||
|
diagnostic.object_id = None;
|
||||||
|
}
|
||||||
|
report.status = "invalid".into();
|
||||||
|
report.exit_code = if disk_refusal { 3 } else { 1 };
|
||||||
|
return Err(Box::new(report));
|
||||||
|
}
|
||||||
|
let explicit_ids: std::collections::BTreeSet<_> = raw
|
||||||
|
.files
|
||||||
|
.values()
|
||||||
|
.flat_map(|text| language::analyze(text).declarations)
|
||||||
|
.filter_map(|d| d.id.map(|id| id.to_string()))
|
||||||
|
.collect();
|
||||||
|
for diagnostic in &mut report.diagnostics {
|
||||||
|
if diagnostic
|
||||||
|
.object_id
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|id| !explicit_ids.contains(id))
|
||||||
|
{
|
||||||
|
diagnostic.object_id = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut compiled = source_project::compile_bundle(&raw, LIMITS).map_err(|diagnostics| {
|
||||||
|
report.status = "invalid".into();
|
||||||
|
report.exit_code = 1;
|
||||||
|
report
|
||||||
|
.diagnostics
|
||||||
|
.extend(diagnostics.into_iter().map(|d| located(d, "candidate")));
|
||||||
|
Box::new(report.clone())
|
||||||
|
})?;
|
||||||
|
report.identities_to_stabilize = raw
|
||||||
|
.files
|
||||||
|
.values()
|
||||||
|
.map(|text| {
|
||||||
|
language::analyze(text)
|
||||||
|
.declarations
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| d.id.is_none())
|
||||||
|
.count()
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
report.migration = compiled.decoded.migration.clone();
|
||||||
|
let mutable = !matches!(operation, Operation::Check);
|
||||||
|
if let Operation::RenameAlias { old, new } = operation {
|
||||||
|
let project = &compiled.decoded.project;
|
||||||
|
let Some((id, _)) = project
|
||||||
|
.identities()
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, alias)| *alias == old)
|
||||||
|
else {
|
||||||
|
report.fail(
|
||||||
|
1,
|
||||||
|
"unknown_alias",
|
||||||
|
format!("No declaration has alias '{old}'."),
|
||||||
|
);
|
||||||
|
return Err(Box::new(report));
|
||||||
|
};
|
||||||
|
let command = prepare_command(project, &Command::SetAlias(id, new)).map_err(|error| {
|
||||||
|
report.fail(1, "semantic_command_rejected", error.to_string());
|
||||||
|
Box::new(report.clone())
|
||||||
|
})?;
|
||||||
|
compiled = source_project::update_bundle_with_identity_map(
|
||||||
|
&compiled,
|
||||||
|
&command.project,
|
||||||
|
&command.identity_map,
|
||||||
|
LIMITS,
|
||||||
|
)
|
||||||
|
.map_err(|diagnostics| {
|
||||||
|
report.status = "invalid".into();
|
||||||
|
report.exit_code = 1;
|
||||||
|
report
|
||||||
|
.diagnostics
|
||||||
|
.extend(diagnostics.into_iter().map(|d| located(d, "candidate")));
|
||||||
|
Box::new(report.clone())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
if mutable {
|
||||||
|
for (path, after) in &compiled.bundle.files {
|
||||||
|
let before = raw.files.get(path).cloned().unwrap_or_default();
|
||||||
|
if &before != after {
|
||||||
|
report.changes.push(Change {
|
||||||
|
path: path.clone(),
|
||||||
|
diff: unified_diff(path, &before, after),
|
||||||
|
before,
|
||||||
|
after: after.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
report.status = "preview".into();
|
||||||
|
report.notes.push("This invocation's complete proposed changed-file texts and diffs are reported. Without --write nothing is saved; a later invocation may allocate different missing UUIDs. This is not a saved plan or a digest of unchanged dependencies.".into());
|
||||||
|
}
|
||||||
|
report.notes.push("Diagnostic object IDs are omitted for declarations without authored IDs; lint's temporary identities are not the candidate's identities.".into());
|
||||||
|
report.notes.push("Disk writes use the core journal/conflict checks. This is not cross-process atomic visibility or a power-loss durability guarantee; pending saves are never implicitly recovered. A failed write may require explicit journal recovery.".into());
|
||||||
|
Ok(Prepared {
|
||||||
|
report,
|
||||||
|
snapshot,
|
||||||
|
compiled,
|
||||||
|
mutable,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One bounded hunk per file: common prefix/suffix plus three context lines.
|
||||||
|
/// Linear in input size; no quadratic LCS allocation for large modules.
|
||||||
|
fn unified_diff(path: &str, before: &str, after: &str) -> String {
|
||||||
|
let a: Vec<_> = before.split_inclusive('\n').collect();
|
||||||
|
let b: Vec<_> = after.split_inclusive('\n').collect();
|
||||||
|
let prefix = a.iter().zip(&b).take_while(|(a, b)| a == b).count();
|
||||||
|
let suffix = a[prefix..]
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.zip(b[prefix..].iter().rev())
|
||||||
|
.take_while(|(a, b)| a == b)
|
||||||
|
.count();
|
||||||
|
let start = prefix.saturating_sub(3);
|
||||||
|
let a_end = (a.len() - suffix + 3).min(a.len());
|
||||||
|
let b_end = (b.len() - suffix + 3).min(b.len());
|
||||||
|
let a_start = if a_end == start { start } else { start + 1 };
|
||||||
|
let b_start = if b_end == start { start } else { start + 1 };
|
||||||
|
let mut out = format!(
|
||||||
|
"--- a/{path}\n+++ b/{path}\n@@ -{a_start},{} +{b_start},{} @@\n",
|
||||||
|
a_end - start,
|
||||||
|
b_end - start
|
||||||
|
);
|
||||||
|
let mut emit = |prefix: char, line: &str| {
|
||||||
|
out.push(prefix);
|
||||||
|
out.push_str(line);
|
||||||
|
if !line.ends_with('\n') {
|
||||||
|
out.push_str("\n\\ No newline at end of file\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for line in &a[start..prefix] {
|
||||||
|
emit(' ', line);
|
||||||
|
}
|
||||||
|
for line in &a[prefix..a.len() - suffix] {
|
||||||
|
emit('-', line);
|
||||||
|
}
|
||||||
|
for line in &b[prefix..b.len() - suffix] {
|
||||||
|
emit('+', line);
|
||||||
|
}
|
||||||
|
for line in &a[a.len() - suffix..a_end] {
|
||||||
|
emit(' ', line);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Arguments {
|
||||||
|
pub entry: PathBuf,
|
||||||
|
pub operation: Operation,
|
||||||
|
pub write: bool,
|
||||||
|
pub json: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const HELP: &str = "arcadia check ROOT [--json]\narcadia lint ROOT [--json]\narcadia stabilize ROOT [--write] [--json]\narcadia rename-alias ROOT OLD NEW [--write] [--json]\n\nROOT is an explicit .arcadia entry file; includes resolve relative to it.\nMutation commands preview by default. --write applies this invocation's candidate.\nMissing UUIDs in a preview are provisional until written. No canonical formatting.\nExit: 0 valid/success (engineering warnings allowed), 1 invalid source or semantic\ncommand rejection, 2 usage, 3 disk/path/conflict/pending-journal/output failure.\nNo implicit journal recovery. Limits: 64 MiB, 256 files, include depth32/edges1024,\n100000 graph objects, 250000 records, 500000 ordered references.\n";
|
||||||
|
|
||||||
|
pub fn arguments(args: &[std::ffi::OsString]) -> Result<Arguments, String> {
|
||||||
|
let mut positional = Vec::new();
|
||||||
|
let (mut write, mut json) = (false, false);
|
||||||
|
for arg in args {
|
||||||
|
if arg == "--write" && !write {
|
||||||
|
write = true;
|
||||||
|
} else if arg == "--json" && !json {
|
||||||
|
json = true;
|
||||||
|
} else if arg.to_string_lossy().starts_with('-') {
|
||||||
|
return Err(format!(
|
||||||
|
"Unknown or repeated option: {}",
|
||||||
|
arg.to_string_lossy()
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
positional.push(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let command = positional.first().and_then(|s| s.to_str()).unwrap_or("");
|
||||||
|
let operation = match (command, positional.len()) {
|
||||||
|
("check" | "lint", 2) if !write => Operation::Check,
|
||||||
|
("stabilize", 2) => Operation::Stabilize,
|
||||||
|
("rename-alias", 4) => Operation::RenameAlias {
|
||||||
|
old: positional[2].to_str().ok_or("Alias must be UTF-8")?.into(),
|
||||||
|
new: positional[3].to_str().ok_or("Alias must be UTF-8")?.into(),
|
||||||
|
},
|
||||||
|
_ => return Err(HELP.into()),
|
||||||
|
};
|
||||||
|
Ok(Arguments {
|
||||||
|
entry: PathBuf::from(positional[1]),
|
||||||
|
operation,
|
||||||
|
write,
|
||||||
|
json,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
use oa_cli::{Report, arguments, prepare};
|
||||||
|
use std::{
|
||||||
|
io::{self, Write},
|
||||||
|
path::Path,
|
||||||
|
process::ExitCode,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn human_report(report: &Report, show_diff: bool) -> io::Result<()> {
|
||||||
|
let mut err = io::stderr().lock();
|
||||||
|
for d in &report.diagnostics {
|
||||||
|
let at = d
|
||||||
|
.location
|
||||||
|
.as_ref()
|
||||||
|
.map(|l| {
|
||||||
|
format!(
|
||||||
|
"{}:{}:{}",
|
||||||
|
l.path,
|
||||||
|
l.line.unwrap_or(0),
|
||||||
|
l.column.unwrap_or(0)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| report.entry.clone());
|
||||||
|
writeln!(
|
||||||
|
err,
|
||||||
|
"{at}: {}[{}] ({}): {}",
|
||||||
|
d.severity, d.code, d.coordinate_space, d.message
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
writeln!(
|
||||||
|
err,
|
||||||
|
"{}: {} file changes; {} identities to stabilize",
|
||||||
|
report.status,
|
||||||
|
report.changes.len(),
|
||||||
|
report.identities_to_stabilize
|
||||||
|
)?;
|
||||||
|
if let Some(migration) = &report.migration {
|
||||||
|
writeln!(
|
||||||
|
err,
|
||||||
|
"Source migration: schema {} → {} (separate from identity stabilization)",
|
||||||
|
migration.from_schema, migration.to_schema
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
for note in &report.notes {
|
||||||
|
writeln!(err, "{note}")?;
|
||||||
|
}
|
||||||
|
if show_diff {
|
||||||
|
let mut out = io::stdout().lock();
|
||||||
|
for change in &report.changes {
|
||||||
|
write!(out, "{}", change.diff)?;
|
||||||
|
}
|
||||||
|
out.flush()?;
|
||||||
|
}
|
||||||
|
err.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run() -> io::Result<u8> {
|
||||||
|
let args: Vec<_> = std::env::args_os().skip(1).collect();
|
||||||
|
if args.len() == 1 && (args[0] == "--help" || args[0] == "-h") {
|
||||||
|
io::stdout().write_all(oa_cli::HELP.as_bytes())?;
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
if args.len() == 1 && args[0] == "--version" {
|
||||||
|
writeln!(io::stdout(), "arcadia {}", env!("CARGO_PKG_VERSION"))?;
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let json = args.iter().any(|a| a == "--json");
|
||||||
|
let mut displayed = false;
|
||||||
|
let report = match arguments(&args) {
|
||||||
|
Err(error) => Report::failure(Path::new(""), "usage", 2, "usage", error),
|
||||||
|
Ok(args) => match prepare(&args.entry, args.operation) {
|
||||||
|
Err(report) => *report,
|
||||||
|
Ok(prepared) => {
|
||||||
|
if args.write {
|
||||||
|
if !args.json {
|
||||||
|
human_report(prepared.report(), true)?;
|
||||||
|
displayed = true;
|
||||||
|
}
|
||||||
|
prepared.write()
|
||||||
|
} else {
|
||||||
|
prepared.into_report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if json {
|
||||||
|
serde_json::to_writer_pretty(io::stdout().lock(), &report)?;
|
||||||
|
writeln!(io::stdout())?;
|
||||||
|
} else {
|
||||||
|
human_report(&report, !displayed)?;
|
||||||
|
}
|
||||||
|
Ok(report.exit_code)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
match run() {
|
||||||
|
Ok(code) => ExitCode::from(code),
|
||||||
|
Err(error) => {
|
||||||
|
let _ = writeln!(io::stderr(), "Output failed: {error}");
|
||||||
|
ExitCode::from(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,425 @@
|
|||||||
|
use oa_cli::{LIMITS, Operation, Prepared, prepare};
|
||||||
|
use oa_core::{Id, Project, source_files, source_project};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Fixture(PathBuf);
|
||||||
|
impl Fixture {
|
||||||
|
fn new() -> Self {
|
||||||
|
let path = std::env::temp_dir().join(format!("arcadia-cli-test-{}", Id::new_v4()));
|
||||||
|
fs::create_dir(&path).unwrap();
|
||||||
|
Self(path)
|
||||||
|
}
|
||||||
|
fn put(&self, name: &str, text: &str) {
|
||||||
|
let path = self.0.join(name);
|
||||||
|
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||||
|
fs::write(path, text).unwrap();
|
||||||
|
}
|
||||||
|
fn entry(&self) -> PathBuf {
|
||||||
|
self.0.join("project.arcadia")
|
||||||
|
}
|
||||||
|
fn read(&self, name: &str) -> String {
|
||||||
|
fs::read_to_string(self.0.join(name)).unwrap()
|
||||||
|
}
|
||||||
|
fn prepare(&self, op: Operation) -> Prepared {
|
||||||
|
prepare(&self.entry(), op).unwrap_or_else(|report| panic!("{report:#?}"))
|
||||||
|
}
|
||||||
|
fn model(&self) -> Project {
|
||||||
|
let disk = source_files::load_project(&self.entry(), LIMITS).unwrap();
|
||||||
|
source_project::compile_bundle(&disk.reachable_bundle(), LIMITS)
|
||||||
|
.unwrap()
|
||||||
|
.decoded
|
||||||
|
.project
|
||||||
|
}
|
||||||
|
fn cli(&self, args: &[&str]) -> (i32, Value) {
|
||||||
|
let mut command = Command::new(env!("CARGO_BIN_EXE_arcadia"));
|
||||||
|
command
|
||||||
|
.arg(args[0])
|
||||||
|
.arg(self.entry())
|
||||||
|
.args(&args[1..])
|
||||||
|
.arg("--json");
|
||||||
|
let output = command.output().unwrap();
|
||||||
|
assert!(
|
||||||
|
output.stderr.is_empty(),
|
||||||
|
"{}",
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
);
|
||||||
|
(
|
||||||
|
output.status.code().unwrap(),
|
||||||
|
serde_json::from_slice(&output.stdout).unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for Fixture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = fs::remove_dir_all(&self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIMPLE: &str = "// 雨: authored defaults stay omitted\r\narcadia 7;\r\nproject rescue \"Rescue\" {\r\n oa_entity team \"Team\" {}\r\n}\r\n";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_alarm_example_preview_then_write_and_fixed_point() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
let source = include_str!(
|
||||||
|
"../../../harness/experiments/textual-authoring-20260911/alarm-response.arcadia"
|
||||||
|
);
|
||||||
|
fixture.put("project.arcadia", source);
|
||||||
|
let (code, check) = fixture.cli(&["check"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
assert_eq!(check["diagnostics"], serde_json::json!([]));
|
||||||
|
let (code, preview) = fixture.cli(&["stabilize"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
assert_eq!(preview["identities_to_stabilize"], 40);
|
||||||
|
assert_eq!(preview["status"], "preview");
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), source);
|
||||||
|
assert!(
|
||||||
|
preview["changes"][0]["diff"]
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.contains("+project alarm_response")
|
||||||
|
);
|
||||||
|
let (code, written) = fixture.cli(&["stabilize", "--write"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
let saved = fixture.read("project.arcadia");
|
||||||
|
assert_eq!(written["changes"][0]["after"], saved);
|
||||||
|
let project = fixture.model();
|
||||||
|
assert_eq!(project.identities().len(), 40);
|
||||||
|
let mut stripped = saved.clone();
|
||||||
|
for (id, _) in project.identities() {
|
||||||
|
stripped = stripped.replace(&format!(" id \"{id}\""), "");
|
||||||
|
}
|
||||||
|
assert_eq!(stripped, source, "only identity clauses may be inserted");
|
||||||
|
let (code, again) = fixture.cli(&["stabilize", "--write"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
assert_eq!(again["changes"], serde_json::json!([]));
|
||||||
|
assert_eq!(again["status"], "unchanged");
|
||||||
|
assert_eq!(fixture.model(), project);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn captured_candidate_exact_write_preserves_crlf_comments_and_noop_metadata() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", SIMPLE);
|
||||||
|
let candidate = fixture.prepare(Operation::Stabilize);
|
||||||
|
let expected = candidate.report().changes[0].after.clone();
|
||||||
|
assert_eq!(candidate.report().identities_to_stabilize, 2);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), SIMPLE);
|
||||||
|
assert_eq!(candidate.write().exit_code, 0);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), expected);
|
||||||
|
assert!(expected.contains("// 雨: authored defaults stay omitted\r\n"));
|
||||||
|
assert!(!expected.contains("description"));
|
||||||
|
let metadata = fs::metadata(fixture.entry()).unwrap().modified().unwrap();
|
||||||
|
let report = fixture.prepare(Operation::Stabilize).write();
|
||||||
|
assert_eq!(report.status, "unchanged");
|
||||||
|
assert!(!report.written);
|
||||||
|
assert_eq!(
|
||||||
|
fs::metadata(fixture.entry()).unwrap().modified().unwrap(),
|
||||||
|
metadata
|
||||||
|
);
|
||||||
|
assert_eq!(fs::read_dir(&fixture.0).unwrap().count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_rename_across_modules_changes_only_live_tokens_and_retains_all_identity() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", "arcadia 7; project rescue \"Rescue\" { include \"participants.arcadia\"; include \"work.arcadia\"; }\n");
|
||||||
|
fixture.put(
|
||||||
|
"participants.arcadia",
|
||||||
|
"// @team is quoted documentation\noa_entity team \"Team @team\" {}\n",
|
||||||
|
);
|
||||||
|
fixture.put("work.arcadia", "oa_activity assess \"Assess\" {}\noa_activity report \"Report\" {}\nallocation assigned \"Assigned\" { source @team; target @assess; }\ninteraction transfer \"Transfer\" { source @assess; target @report; }\n");
|
||||||
|
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||||
|
let before = fixture.model();
|
||||||
|
let original: BTreeMap<_, _> = ["project.arcadia", "participants.arcadia", "work.arcadia"]
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| (p, fixture.read(p)))
|
||||||
|
.collect();
|
||||||
|
let team = before
|
||||||
|
.identities()
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, alias)| *alias == "team")
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let candidate = fixture.prepare(Operation::RenameAlias {
|
||||||
|
old: "team".into(),
|
||||||
|
new: "rescue_team".into(),
|
||||||
|
});
|
||||||
|
assert_eq!(candidate.report().changes.len(), 2);
|
||||||
|
assert_eq!(fixture.model(), before, "preview cannot mutate");
|
||||||
|
assert_eq!(candidate.write().exit_code, 0);
|
||||||
|
let mut expected = before;
|
||||||
|
expected.elements.get_mut(&team).unwrap().alias = "rescue_team".into();
|
||||||
|
assert_eq!(fixture.model(), expected);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), original["project.arcadia"]);
|
||||||
|
assert_eq!(
|
||||||
|
fixture.read("participants.arcadia"),
|
||||||
|
original["participants.arcadia"].replacen("oa_entity team ", "oa_entity rescue_team ", 1)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fixture.read("work.arcadia"),
|
||||||
|
original["work.arcadia"].replace("source @team;", "source @rescue_team;")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_unicode_raw_edit_is_located_and_never_stabilized_or_written() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
let raw = "arcadia 7; project p \"雨\" {\n oa_activity a \"A\" {}\n allocation alloc \"Alloc\" { source @missing; target @a; }\n}\n";
|
||||||
|
fixture.put("project.arcadia", raw);
|
||||||
|
for command in [
|
||||||
|
vec!["check"],
|
||||||
|
vec!["stabilize", "--write"],
|
||||||
|
vec!["rename-alias", "a", "b", "--write"],
|
||||||
|
] {
|
||||||
|
let (code, report) = fixture.cli(&command);
|
||||||
|
assert_eq!(code, 1);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||||
|
let diagnostics = report["diagnostics"].as_array().unwrap();
|
||||||
|
assert!(!diagnostics.is_empty());
|
||||||
|
for d in diagnostics {
|
||||||
|
assert_eq!(d["coordinate_space"], "input");
|
||||||
|
let at = &d["location"];
|
||||||
|
let start = at["start"].as_u64().unwrap() as usize;
|
||||||
|
assert!(raw.is_char_boundary(start));
|
||||||
|
assert_eq!(
|
||||||
|
at["line"].as_u64().unwrap() as usize,
|
||||||
|
raw[..start].bytes().filter(|b| *b == b'\n').count() + 1
|
||||||
|
);
|
||||||
|
let line = raw[..start]
|
||||||
|
.rsplit_once('\n')
|
||||||
|
.map_or(&raw[..start], |(_, tail)| tail);
|
||||||
|
assert_eq!(
|
||||||
|
at["column"].as_u64().unwrap() as usize,
|
||||||
|
line.chars().count() + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_id_warning_uses_raw_location_and_never_exposes_ephemeral_lint_id() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
let raw = "arcadia 7;\nproject p \"雨\" {\n oa_activity work \"Work\" {}\n}\n";
|
||||||
|
fixture.put("project.arcadia", raw);
|
||||||
|
let (code, result) = fixture.cli(&["lint"]);
|
||||||
|
assert_eq!(code, 0, "engineering warnings do not reject admission");
|
||||||
|
let warning = result["diagnostics"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find(|d| d["code"] == "work_without_behavior_context")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(warning["severity"], "warning");
|
||||||
|
assert_eq!(
|
||||||
|
warning["location"]["start"],
|
||||||
|
raw.find("oa_activity").unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(warning["location"]["line"], 3);
|
||||||
|
assert_eq!(warning["location"]["column"], 5);
|
||||||
|
assert!(warning["object_id"].is_null());
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn captured_dependency_change_refuses_all_writes_even_on_noop() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", SIMPLE);
|
||||||
|
let pending = fixture.prepare(Operation::Stabilize);
|
||||||
|
let external = format!("{SIMPLE}// editor changed this\n");
|
||||||
|
fixture.put("project.arcadia", &external);
|
||||||
|
let result = pending.write();
|
||||||
|
assert_eq!(result.exit_code, 3);
|
||||||
|
assert_eq!(result.diagnostics.last().unwrap().code, "external_change");
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), external);
|
||||||
|
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||||
|
let noop = fixture.prepare(Operation::Stabilize);
|
||||||
|
assert!(noop.report().changes.is_empty());
|
||||||
|
let latest = format!("{}// another edit\n", fixture.read("project.arcadia"));
|
||||||
|
fixture.put("project.arcadia", &latest);
|
||||||
|
assert_eq!(noop.write().exit_code, 3);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), latest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pending_journal_refuses_read_preview_and_write_without_recovery() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", SIMPLE);
|
||||||
|
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||||
|
let noop = fixture.prepare(Operation::Stabilize);
|
||||||
|
let source = fixture.read("project.arcadia");
|
||||||
|
// An opaque incomplete journal must never be interpreted/recovered by CLI.
|
||||||
|
let journal = fixture.0.join(".arcadia-save-project.arcadia.journal");
|
||||||
|
fs::create_dir(&journal).unwrap();
|
||||||
|
fs::write(journal.join("sentinel"), b"retain").unwrap();
|
||||||
|
for op in [Operation::Check, Operation::Stabilize] {
|
||||||
|
let report = prepare(&fixture.entry(), op).err().unwrap();
|
||||||
|
assert_eq!(report.exit_code, 3);
|
||||||
|
assert!(report.diagnostics.iter().any(|d| d.code == "pending_save"));
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
noop.write().exit_code,
|
||||||
|
3,
|
||||||
|
"saver must recheck after preview"
|
||||||
|
);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), source);
|
||||||
|
assert_eq!(fs::read(journal.join("sentinel")).unwrap(), b"retain");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejected_alias_and_same_alias_preserve_source() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", SIMPLE);
|
||||||
|
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||||
|
let source = fixture.read("project.arcadia");
|
||||||
|
for (old, new) in [
|
||||||
|
("team", "rescue"),
|
||||||
|
("team", "invalid alias"),
|
||||||
|
("absent", "valid"),
|
||||||
|
] {
|
||||||
|
let (code, result) = fixture.cli(&["rename-alias", old, new, "--write"]);
|
||||||
|
assert_eq!(code, 1);
|
||||||
|
assert_eq!(result["status"], "rejected");
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), source);
|
||||||
|
}
|
||||||
|
let (code, result) = fixture.cli(&["rename-alias", "team", "team", "--write"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
assert_eq!(result["status"], "unchanged");
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_shows_missing_identity_and_separate_source_migration() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
let old = "// retain\narcadia 5; project p \"P\" { oa_entity team \"Team\" {} }\n";
|
||||||
|
fixture.put("project.arcadia", old);
|
||||||
|
let candidate = fixture.prepare(Operation::RenameAlias {
|
||||||
|
old: "team".into(),
|
||||||
|
new: "crew".into(),
|
||||||
|
});
|
||||||
|
let report = candidate.report();
|
||||||
|
assert_eq!(report.identities_to_stabilize, 2);
|
||||||
|
assert_eq!(report.migration.as_ref().unwrap().from_schema, 5);
|
||||||
|
assert_eq!(report.migration.as_ref().unwrap().to_schema, 7);
|
||||||
|
assert!(report.changes[0].diff.contains("-arcadia 5;"));
|
||||||
|
assert!(report.changes[0].diff.contains("+arcadia 7;"));
|
||||||
|
assert!(report.changes[0].after.contains("oa_entity crew"));
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), old);
|
||||||
|
assert_eq!(candidate.write().exit_code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn entry_is_explicit_and_machine_readable_usage_errors_do_not_write() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", SIMPLE);
|
||||||
|
let output = Command::new(env!("CARGO_BIN_EXE_arcadia"))
|
||||||
|
.current_dir(&fixture.0)
|
||||||
|
.args(["check", "--json"])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(output.status.code(), Some(2));
|
||||||
|
let error: Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||||
|
assert_eq!(error["diagnostics"][0]["code"], "usage");
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), SIMPLE);
|
||||||
|
let (code, _) = fixture.cli(&["check", "--write"]);
|
||||||
|
assert_eq!(code, 2);
|
||||||
|
assert!(Path::new(env!("CARGO_BIN_EXE_arcadia")).is_file());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loader_encoding_missing_dependency_and_invalid_old_source_keep_original_bytes() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
let entry = "arcadia 7; project p \"P\" { include \"part.arcadia\"; }";
|
||||||
|
fixture.put("project.arcadia", entry);
|
||||||
|
let (code, report) = fixture.cli(&["stabilize", "--write"]);
|
||||||
|
assert_eq!(code, 3, "a disk dependency could not be read");
|
||||||
|
assert!(
|
||||||
|
report["diagnostics"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.any(|d| d["code"] == "missing_include")
|
||||||
|
);
|
||||||
|
fs::write(fixture.0.join("part.arcadia"), [0xff, 0xfe]).unwrap();
|
||||||
|
let (code, report) = fixture.cli(&["stabilize", "--write"]);
|
||||||
|
assert_eq!(code, 1, "invalid encoding is invalid raw source");
|
||||||
|
assert!(
|
||||||
|
report["diagnostics"]
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.any(|d| d["code"] == "source_encoding")
|
||||||
|
);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), entry);
|
||||||
|
assert_eq!(
|
||||||
|
fs::read(fixture.0.join("part.arcadia")).unwrap(),
|
||||||
|
[0xff, 0xfe]
|
||||||
|
);
|
||||||
|
for raw in [
|
||||||
|
"arcadia 5; project p \"P\" { oa_entity a \"A\" { ??? } }",
|
||||||
|
"arcadia 2; project p \"P\" {}",
|
||||||
|
] {
|
||||||
|
fixture.put("project.arcadia", raw);
|
||||||
|
let (code, _) = fixture.cli(&["stabilize", "--write"]);
|
||||||
|
assert_eq!(code, 1);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_element_alias_detached_file_and_unchanged_dependency_conflict() {
|
||||||
|
let fixture = Fixture::new();
|
||||||
|
fixture.put("project.arcadia", "arcadia 7; project p \"P\" { include \"part.arcadia\"; oa_activity a \"A\" {} allocation responsible \"Responsible\" { source @ // keep edge comment\nteam; target @a; } }");
|
||||||
|
fixture.put("part.arcadia", "oa_entity team \"Team\" {}");
|
||||||
|
fixture.put(
|
||||||
|
"detached.arcadia",
|
||||||
|
"invalid detached draft must stay exact 雨",
|
||||||
|
);
|
||||||
|
assert_eq!(fixture.prepare(Operation::Stabilize).write().exit_code, 0);
|
||||||
|
let before = fixture.model();
|
||||||
|
let relation = before
|
||||||
|
.identities()
|
||||||
|
.into_iter()
|
||||||
|
.find(|(_, alias)| *alias == "responsible")
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let (code, _) = fixture.cli(&["rename-alias", "responsible", "performs", "--write"]);
|
||||||
|
assert_eq!(code, 0);
|
||||||
|
let mut expected = before;
|
||||||
|
expected.relations.get_mut(&relation).unwrap().alias = "performs".into();
|
||||||
|
assert_eq!(fixture.model(), expected);
|
||||||
|
assert!(
|
||||||
|
fixture
|
||||||
|
.read("project.arcadia")
|
||||||
|
.contains("@ // keep edge comment\nteam")
|
||||||
|
);
|
||||||
|
let captured = fixture.prepare(Operation::RenameAlias {
|
||||||
|
old: "p".into(),
|
||||||
|
new: "rescue".into(),
|
||||||
|
});
|
||||||
|
assert!(
|
||||||
|
captured
|
||||||
|
.report()
|
||||||
|
.changes
|
||||||
|
.iter()
|
||||||
|
.all(|c| c.path != "part.arcadia")
|
||||||
|
);
|
||||||
|
let root_before = fixture.read("project.arcadia");
|
||||||
|
fixture.put("part.arcadia", "// external unchanged-dependency edit\n");
|
||||||
|
assert_eq!(captured.write().exit_code, 3);
|
||||||
|
assert_eq!(fixture.read("project.arcadia"), root_before);
|
||||||
|
assert_eq!(
|
||||||
|
fixture.read("part.arcadia"),
|
||||||
|
"// external unchanged-dependency edit\n"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fixture.read("detached.arcadia"),
|
||||||
|
"invalid detached draft must stay exact 雨"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -854,8 +854,12 @@ impl App {
|
|||||||
let scenario_selection = self
|
let scenario_selection = self
|
||||||
.behavior
|
.behavior
|
||||||
.scenario
|
.scenario
|
||||||
.created_selection(&command)
|
.created_selection(self.project(), &command)
|
||||||
.or_else(|| self.behavior.topology.created_selection(&command));
|
.or_else(|| {
|
||||||
|
self.behavior
|
||||||
|
.topology
|
||||||
|
.created_selection(self.project(), &command)
|
||||||
|
});
|
||||||
match self.document.execute(command) {
|
match self.document.execute(command) {
|
||||||
Ok(selected) => {
|
Ok(selected) => {
|
||||||
if let Some(created) = created {
|
if let Some(created) = created {
|
||||||
@@ -1031,8 +1035,10 @@ impl App {
|
|||||||
window::close_requests().map(|id| Message::Request(Pending::Close(id))),
|
window::close_requests().map(|id| Message::Request(Pending::Close(id))),
|
||||||
window::resize_events().map(|(_, size)| Message::WindowResized(size)),
|
window::resize_events().map(|(_, size)| Message::WindowResized(size)),
|
||||||
architecture_authoring::subscription(self),
|
architecture_authoring::subscription(self),
|
||||||
behavior::creation::subscription(self.behavior.creation.has_pending())
|
behavior::creation::subscription(
|
||||||
.map(|()| Message::Escape),
|
self.behavior.creation.has_pending() || self.behavior.source_active(),
|
||||||
|
)
|
||||||
|
.map(|()| Message::Escape),
|
||||||
iced::event::listen_with(|event, status, _| {
|
iced::event::listen_with(|event, status, _| {
|
||||||
let iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
let iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
key,
|
key,
|
||||||
@@ -1519,6 +1525,37 @@ impl App {
|
|||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
match mutation {
|
match mutation {
|
||||||
|
CanvasEvent::First(_, tool) => {
|
||||||
|
let empty = match tool {
|
||||||
|
scenario::Tool::Lifeline => !self
|
||||||
|
.project()
|
||||||
|
.children(context.scenario)
|
||||||
|
.any(|e| e.kind() == Kind::Lifeline),
|
||||||
|
scenario::Tool::Message | scenario::Tool::Action => {
|
||||||
|
self.project()
|
||||||
|
.children(context.scenario)
|
||||||
|
.any(|e| e.kind() == Kind::Lifeline)
|
||||||
|
&& !self
|
||||||
|
.project()
|
||||||
|
.children(context.scenario)
|
||||||
|
.any(|e| e.kind() == Kind::Occurrence)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !empty {
|
||||||
|
self.status="This scenario already has content. Use its current authoring tools.".into();
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
let mut state = std::mem::take(&mut self.behavior.scenario);
|
||||||
|
let result = state.update(
|
||||||
|
self.project(),
|
||||||
|
scenario::Event::Start(context.scenario, tool),
|
||||||
|
);
|
||||||
|
self.behavior.scenario = state;
|
||||||
|
if let Err(error) = result {
|
||||||
|
self.status = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
CanvasEvent::BeginOccurrence {
|
CanvasEvent::BeginOccurrence {
|
||||||
tool,
|
tool,
|
||||||
slot,
|
slot,
|
||||||
@@ -1754,7 +1791,15 @@ impl App {
|
|||||||
return self.update(Message::Add(child));
|
return self.update(Message::Add(child));
|
||||||
}
|
}
|
||||||
Message::Escape => {
|
Message::Escape => {
|
||||||
if self.behavior.creation.has_pending() {
|
if self.behavior.source_back() {
|
||||||
|
self.status =
|
||||||
|
"Returned to the unchanged parent form; no source was created.".into();
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
if self.behavior.source_active() {
|
||||||
|
self.behavior.acknowledge_commit();
|
||||||
|
self.status = "Behavior form cancelled; no staged source was created.".into();
|
||||||
|
} else if self.behavior.creation.has_pending() {
|
||||||
self.behavior.creation.acknowledge_commit();
|
self.behavior.creation.acknowledge_commit();
|
||||||
self.status = "Behavior creation cancelled".into();
|
self.status = "Behavior creation cancelled".into();
|
||||||
}
|
}
|
||||||
@@ -2321,10 +2366,13 @@ impl App {
|
|||||||
self.status = "Apply or cancel the current draft before starting a new behavior. Your inputs are retained.".into();
|
self.status = "Apply or cancel the current draft before starting a new behavior. Your inputs are retained.".into();
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
if !creating && event.starts_form() && !self.behavior.has_pending() && !self.apply()
|
if (event.starts_form() || (event.is_source() && !event.is_source_back()))
|
||||||
|
&& (self.source_dirty() || self.draft_dirty())
|
||||||
{
|
{
|
||||||
|
self.status="Apply or cancel the other editors before starting this form. Your inputs are retained.".into();
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
let source_was_open = self.behavior.source_open();
|
||||||
if event.is_submit() && (self.source_dirty() || self.draft_dirty()) {
|
if event.is_submit() && (self.source_dirty() || self.draft_dirty()) {
|
||||||
self.status = "Revert the other drafts before applying the behavior form. Your inputs are retained.".into();
|
self.status = "Revert the other drafts before applying the behavior form. Your inputs are retained.".into();
|
||||||
return Task::none();
|
return Task::none();
|
||||||
@@ -2342,6 +2390,11 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
|
if !source_was_open && self.behavior.source_open() {
|
||||||
|
return iced::widget::operation::focus(
|
||||||
|
behavior::source_draft::NAME_INPUT,
|
||||||
|
);
|
||||||
|
}
|
||||||
if focus_name {
|
if focus_name {
|
||||||
return iced::widget::operation::focus(behavior::creation::NAME_INPUT);
|
return iced::widget::operation::focus(behavior::creation::NAME_INPUT);
|
||||||
}
|
}
|
||||||
@@ -2537,13 +2590,19 @@ impl App {
|
|||||||
Message::Undo | Message::Redo => {
|
Message::Undo | Message::Redo => {
|
||||||
let redo = matches!(message, Message::Redo);
|
let redo = matches!(message, Message::Redo);
|
||||||
if self.apply() {
|
if self.apply() {
|
||||||
if redo {
|
let changed = if redo {
|
||||||
self.document.redo();
|
self.document.redo()
|
||||||
} else {
|
} else {
|
||||||
self.document.undo();
|
self.document.undo()
|
||||||
|
};
|
||||||
|
if changed {
|
||||||
|
self.sync();
|
||||||
}
|
}
|
||||||
self.sync();
|
self.status = if !changed && redo {
|
||||||
self.status = if redo {
|
"Nothing to redo"
|
||||||
|
} else if !changed {
|
||||||
|
"Nothing to undo"
|
||||||
|
} else if redo {
|
||||||
"Model change redone"
|
"Model change redone"
|
||||||
} else {
|
} else {
|
||||||
"Model change undone"
|
"Model change undone"
|
||||||
@@ -3231,6 +3290,52 @@ impl App {
|
|||||||
id,
|
id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
// Refining a leaf moves its explicit responsibilities to the new leaf.
|
||||||
|
// Later children inherit only the existing unambiguous performer summary.
|
||||||
|
if self.create_kind.is_work()
|
||||||
|
&& let Some(owner) = parent
|
||||||
|
&& self
|
||||||
|
.project()
|
||||||
|
.elements
|
||||||
|
.get(&owner)
|
||||||
|
.is_some_and(|e| e.layer() == self.layer && e.kind() == self.create_kind)
|
||||||
|
{
|
||||||
|
let performer = oa_core::validation::allocation_summary(self.project(), owner);
|
||||||
|
let leaf = !self
|
||||||
|
.project()
|
||||||
|
.children(owner)
|
||||||
|
.any(|e| e.kind() == self.create_kind);
|
||||||
|
let responsibilities: Vec<_> = self
|
||||||
|
.project()
|
||||||
|
.relations
|
||||||
|
.values()
|
||||||
|
.filter(|r| {
|
||||||
|
r.target() == owner
|
||||||
|
&& matches!(
|
||||||
|
r.kind(),
|
||||||
|
RelationKind::Allocation | RelationKind::RoleAssignment
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if leaf && !responsibilities.is_empty() {
|
||||||
|
for original in responsibilities {
|
||||||
|
let mut responsibility = original.clone();
|
||||||
|
match &mut responsibility.body {
|
||||||
|
RelationBody::Allocation { target, .. }
|
||||||
|
| RelationBody::RoleAssignment { target, .. } => *target = id,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
commands.push(Command::EditRelation(original.id, responsibility));
|
||||||
|
}
|
||||||
|
} else if let Some(performer) = performer {
|
||||||
|
commands.push(Command::Connect(
|
||||||
|
RelationKind::Allocation,
|
||||||
|
"Performs".into(),
|
||||||
|
performer,
|
||||||
|
id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
if self.create_kind == Kind::Occurrence
|
if self.create_kind == Kind::Occurrence
|
||||||
&& let Some(owner) = parent.and_then(|id| self.project().elements.get(&id))
|
&& let Some(owner) = parent.and_then(|id| self.project().elements.get(&id))
|
||||||
&& let Some(steps) = owner.steps()
|
&& let Some(steps) = owner.steps()
|
||||||
@@ -3248,19 +3353,13 @@ impl App {
|
|||||||
.get(&diagram)
|
.get(&diagram)
|
||||||
.is_none_or(|d| d.projection.includes(self.create_kind))
|
.is_none_or(|d| d.projection.includes(self.create_kind))
|
||||||
{
|
{
|
||||||
let mut p = self.project().clone();
|
let mut p = match execute_batch(self.project(), &commands) {
|
||||||
for command in &commands {
|
Ok(candidate) => candidate,
|
||||||
if let Command::CreateDiagram(d) = command {
|
Err(error) => {
|
||||||
p.diagrams.insert(d.id, d.clone());
|
self.status = error.to_string();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
p.elements.insert(
|
|
||||||
id,
|
|
||||||
match &commands[0] {
|
|
||||||
Command::CreateElement(e) => e.clone(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let owner = if allocated {
|
let owner = if allocated {
|
||||||
Some(self.selected)
|
Some(self.selected)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -214,6 +214,11 @@ impl App {
|
|||||||
.is_some_and(architecture_authoring::State::inline)
|
.is_some_and(architecture_authoring::State::inline)
|
||||||
{
|
{
|
||||||
Task::batch([lint, iced::widget::operation::focus("architecture-name")])
|
Task::batch([lint, iced::widget::operation::focus("architecture-name")])
|
||||||
|
} else if self.page == Page::Behavior && self.behavior.source_open() {
|
||||||
|
Task::batch([
|
||||||
|
lint,
|
||||||
|
iced::widget::operation::focus(behavior::source_draft::NAME_INPUT),
|
||||||
|
])
|
||||||
} else if self.page == Page::Behavior && self.behavior.creation.created().is_some() {
|
} else if self.page == Page::Behavior && self.behavior.creation.created().is_some() {
|
||||||
Task::batch([
|
Task::batch([
|
||||||
lint,
|
lint,
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ use iced::{
|
|||||||
};
|
};
|
||||||
use oa_core::*;
|
use oa_core::*;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod contextual_source_tests;
|
||||||
pub(crate) mod creation;
|
pub(crate) mod creation;
|
||||||
pub(crate) mod scenario;
|
pub(crate) mod scenario;
|
||||||
|
mod scenario_lifecycle;
|
||||||
|
pub(crate) mod source_draft;
|
||||||
pub(crate) mod state_canvas;
|
pub(crate) mod state_canvas;
|
||||||
pub(crate) mod state_edit;
|
pub(crate) mod state_edit;
|
||||||
pub(crate) mod state_menu;
|
pub(crate) mod state_menu;
|
||||||
@@ -739,6 +743,29 @@ pub enum Event {
|
|||||||
Submit,
|
Submit,
|
||||||
}
|
}
|
||||||
impl Event {
|
impl Event {
|
||||||
|
pub fn is_source_back(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Creation(creation::Event::Source(source_draft::Event::Edit(
|
||||||
|
_,
|
||||||
|
source_draft::Edit::Back
|
||||||
|
))) | Self::Scenario(scenario::Event::Source(source_draft::Event::Edit(
|
||||||
|
_,
|
||||||
|
source_draft::Edit::Back
|
||||||
|
))) | Self::Topology(topology::Event::Source(source_draft::Event::Edit(
|
||||||
|
_,
|
||||||
|
source_draft::Edit::Back
|
||||||
|
)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn is_source(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Creation(creation::Event::Source(_))
|
||||||
|
| Self::Scenario(scenario::Event::Source(_))
|
||||||
|
| Self::Topology(topology::Event::Source(_))
|
||||||
|
)
|
||||||
|
}
|
||||||
pub fn starts_form(&self) -> bool {
|
pub fn starts_form(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
@@ -869,6 +896,18 @@ impl State {
|
|||||||
|
|
||||||
/// Clear the active form after its prepared command commits successfully.
|
/// Clear the active form after its prepared command commits successfully.
|
||||||
/// Rejected commands must leave the form available for repair and retry.
|
/// Rejected commands must leave the form available for repair and retry.
|
||||||
|
pub fn source_active(&self) -> bool {
|
||||||
|
self.creation.source_active()
|
||||||
|
|| self.scenario.source_active()
|
||||||
|
|| self.topology.source_active()
|
||||||
|
}
|
||||||
|
pub fn source_open(&self) -> bool {
|
||||||
|
self.creation.source_open() || self.scenario.source_open() || self.topology.source_open()
|
||||||
|
}
|
||||||
|
pub fn source_back(&mut self) -> bool {
|
||||||
|
self.creation.source_back() || self.scenario.source_back() || self.topology.source_back()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn acknowledge_commit(&mut self) {
|
pub fn acknowledge_commit(&mut self) {
|
||||||
self.creation.acknowledge_commit();
|
self.creation.acknowledge_commit();
|
||||||
self.topology.acknowledge_commit();
|
self.topology.acknowledge_commit();
|
||||||
@@ -916,6 +955,40 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
|||||||
if let Some(card) = app.behavior.creation.view(p) {
|
if let Some(card) = app.behavior.creation.view(p) {
|
||||||
return card.map(|event| Message::Behavior(Event::Creation(event)));
|
return card.map(|event| Message::Behavior(Event::Creation(event)));
|
||||||
}
|
}
|
||||||
|
if let Some(owner) = app.behavior.pending_owner() {
|
||||||
|
let kind = p.elements.get(&owner).map(oa_core::Element::kind);
|
||||||
|
let unavailable = p.layer_of(owner) != Some(app.layer)
|
||||||
|
|| (app.behavior.scenario.pending_owner().is_some() && kind != Some(Kind::Scenario))
|
||||||
|
|| (app.behavior.topology.pending_owner().is_some()
|
||||||
|
&& !matches!(kind, Some(Kind::Process | Kind::FunctionalChain)));
|
||||||
|
if unavailable {
|
||||||
|
let mut out=column![text("The pending behavior owner is unavailable").size(20),text("Your inputs are retained. Return from the source form or cancel this parent draft before choosing another behavior.").size(13)].spacing(12);
|
||||||
|
if let Some(name) = app
|
||||||
|
.behavior
|
||||||
|
.scenario
|
||||||
|
.pending_name()
|
||||||
|
.or_else(|| app.behavior.topology.pending_name())
|
||||||
|
{
|
||||||
|
out =
|
||||||
|
out.push(text(format!("Pending consumer name: {}", short(name, 80))).size(14));
|
||||||
|
}
|
||||||
|
if let Some(child) = app.behavior.scenario.source_view(p) {
|
||||||
|
out = out.push(child.map(|e| Message::Behavior(Event::Scenario(e))));
|
||||||
|
} else if let Some(child) = app.behavior.topology.source_view(p) {
|
||||||
|
out = out.push(child.map(|e| Message::Behavior(Event::Topology(e))));
|
||||||
|
} else {
|
||||||
|
let cancel = if app.behavior.scenario.pending_owner().is_some() {
|
||||||
|
Event::Scenario(scenario::Event::Cancel)
|
||||||
|
} else if app.behavior.topology.pending_owner().is_some() {
|
||||||
|
Event::Topology(topology::Event::Cancel)
|
||||||
|
} else {
|
||||||
|
Event::States(states::Event::Cancel)
|
||||||
|
};
|
||||||
|
out = out.push(button("Cancel retained draft").on_press(Message::Behavior(cancel)));
|
||||||
|
}
|
||||||
|
return container(out).padding(16).width(Fill).height(Fill).into();
|
||||||
|
}
|
||||||
|
}
|
||||||
let choices = p
|
let choices = p
|
||||||
.elements
|
.elements
|
||||||
.values()
|
.values()
|
||||||
@@ -1068,6 +1141,17 @@ struct BoxItem {
|
|||||||
detail: String,
|
detail: String,
|
||||||
marker: Option<Kind>,
|
marker: Option<Kind>,
|
||||||
}
|
}
|
||||||
|
fn box_text_clip(rect: Rectangle, scale: f32, viewport: Size) -> Option<Rectangle> {
|
||||||
|
if !scale.is_finite() || scale <= 0. || rect.width <= 20. || rect.height <= 16. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Rectangle::new(
|
||||||
|
Point::new((rect.x + 10.) * scale, (rect.y + 8.) * scale),
|
||||||
|
Size::new((rect.width - 20.) * scale, (rect.height - 16.) * scale),
|
||||||
|
)
|
||||||
|
.intersection(&Rectangle::new(Point::ORIGIN, viewport))
|
||||||
|
}
|
||||||
|
|
||||||
struct Line {
|
struct Line {
|
||||||
id: Id,
|
id: Id,
|
||||||
points: Vec<Point>,
|
points: Vec<Point>,
|
||||||
@@ -1089,6 +1173,7 @@ struct Scene {
|
|||||||
rows: BTreeMap<Id, (Id, f32)>,
|
rows: BTreeMap<Id, (Id, f32)>,
|
||||||
open_calls: Vec<Point>,
|
open_calls: Vec<Point>,
|
||||||
sequence_bounds: BTreeMap<Id, (f32, f32)>,
|
sequence_bounds: BTreeMap<Id, (f32, f32)>,
|
||||||
|
lifecycle: scenario_lifecycle::render::Rendering,
|
||||||
}
|
}
|
||||||
struct SequenceSlot {
|
struct SequenceSlot {
|
||||||
slot: scenario::Slot,
|
slot: scenario::Slot,
|
||||||
@@ -1147,6 +1232,7 @@ impl Scene {
|
|||||||
rows: BTreeMap::new(),
|
rows: BTreeMap::new(),
|
||||||
open_calls: vec![],
|
open_calls: vec![],
|
||||||
sequence_bounds: BTreeMap::new(),
|
sequence_bounds: BTreeMap::new(),
|
||||||
|
lifecycle: scenario_lifecycle::render::Rendering::default(),
|
||||||
};
|
};
|
||||||
let mut lanes = p
|
let mut lanes = p
|
||||||
.children(owner)
|
.children(owner)
|
||||||
@@ -1233,7 +1319,17 @@ impl Scene {
|
|||||||
}
|
}
|
||||||
let mut y = if scenario { 148. } else { 120. };
|
let mut y = if scenario { 148. } else { 120. };
|
||||||
scene.occurrences(p, owner, scenario, &lane_x, &mut y, 0);
|
scene.occurrences(p, owner, scenario, &lane_x, &mut y, 0);
|
||||||
|
scene.size.height = (y + 45.).max(210.);
|
||||||
if scenario {
|
if scenario {
|
||||||
|
scene.lifecycle = scenario_lifecycle::render::Rendering::new(
|
||||||
|
p,
|
||||||
|
owner,
|
||||||
|
&lane_x,
|
||||||
|
&scene.rows,
|
||||||
|
&scene.sequence_bounds,
|
||||||
|
scene.size.height,
|
||||||
|
);
|
||||||
|
scene.lifecycle.align_messages(&mut scene.lines);
|
||||||
let calls: Vec<_> = scene
|
let calls: Vec<_> = scene
|
||||||
.rows
|
.rows
|
||||||
.keys()
|
.keys()
|
||||||
@@ -1258,23 +1354,28 @@ impl Scene {
|
|||||||
scene.activation_sequence(p, p.elements[&call].parent.unwrap(), call, x, None);
|
scene.activation_sequence(p, p.elements[&call].parent.unwrap(), call, x, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scene.size.height = (y + 45.).max(210.);
|
|
||||||
for (id, name, detail) in lanes {
|
for (id, name, detail) in lanes {
|
||||||
let x = lane_x[&id];
|
let x = lane_x[&id];
|
||||||
scene.lines.insert(
|
if !scenario {
|
||||||
0,
|
scene.lines.insert(
|
||||||
Line {
|
0,
|
||||||
id,
|
Line {
|
||||||
points: vec![Point::new(x, 85.), Point::new(x, scene.size.height - 20.)],
|
id,
|
||||||
label: String::new(),
|
points: vec![Point::new(x, 85.), Point::new(x, scene.size.height - 20.)],
|
||||||
label_at: Point::ORIGIN,
|
label: String::new(),
|
||||||
dashed: false,
|
label_at: Point::ORIGIN,
|
||||||
},
|
dashed: false,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
scene.boxes.push(BoxItem {
|
scene.boxes.push(BoxItem {
|
||||||
id,
|
id,
|
||||||
rect: Rectangle::new(Point::new(x - 102., 15.), Size::new(204., 70.)),
|
rect: Rectangle::new(Point::new(x - 102., 15.), Size::new(204., 70.)),
|
||||||
title: name,
|
title: if scenario {
|
||||||
|
format!("Lane · {name}")
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
},
|
||||||
detail,
|
detail,
|
||||||
marker: None,
|
marker: None,
|
||||||
});
|
});
|
||||||
@@ -1316,6 +1417,16 @@ impl Scene {
|
|||||||
let Some(SharedBody::Occurrence { body, .. }) = e.shared() else {
|
let Some(SharedBody::Occurrence { body, .. }) = e.shared() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if scenario
|
||||||
|
&& matches!(body, OccurrenceBody::Exchange {
|
||||||
|
sender: Some(sender), receiver: Some(receiver),
|
||||||
|
message_kind: MessageKind::Create | MessageKind::Delete, ..
|
||||||
|
} if sender == receiver)
|
||||||
|
{
|
||||||
|
// Self lifecycle elbows and their caption need clearance from
|
||||||
|
// the preceding action/state/reference box, before recording rows.
|
||||||
|
*y += 16.;
|
||||||
|
}
|
||||||
if ordered {
|
if ordered {
|
||||||
let slot = scenario::Slot {
|
let slot = scenario::Slot {
|
||||||
parent: owner,
|
parent: owner,
|
||||||
@@ -1535,17 +1646,27 @@ impl Scene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn activation_segment(&mut self, call: Id, x: f32, top: f32, bottom: f32) {
|
fn activation_segment(
|
||||||
if bottom <= top {
|
&mut self,
|
||||||
return;
|
call: Id,
|
||||||
|
scope: Id,
|
||||||
|
receiver: Id,
|
||||||
|
x: f32,
|
||||||
|
top: f32,
|
||||||
|
bottom: f32,
|
||||||
|
) {
|
||||||
|
for span in self.lifecycle.alive_spans(receiver, scope, top, bottom) {
|
||||||
|
self.boxes.push(BoxItem {
|
||||||
|
id: call,
|
||||||
|
rect: Rectangle::new(
|
||||||
|
Point::new(x - 4., span.start),
|
||||||
|
Size::new(8., span.end - span.start),
|
||||||
|
),
|
||||||
|
title: String::new(),
|
||||||
|
detail: String::new(),
|
||||||
|
marker: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self.boxes.push(BoxItem {
|
|
||||||
id: call,
|
|
||||||
rect: Rectangle::new(Point::new(x - 4., top), Size::new(8., bottom - top)),
|
|
||||||
title: String::new(),
|
|
||||||
detail: String::new(),
|
|
||||||
marker: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
/// Follow a single correlation through the ordered tree. Branches are drawn
|
/// Follow a single correlation through the ordered tree. Branches are drawn
|
||||||
/// as independent segments, never as one chronological stretch through ALT
|
/// as independent segments, never as one chronological stretch through ALT
|
||||||
@@ -1558,6 +1679,18 @@ impl Scene {
|
|||||||
x: f32,
|
x: f32,
|
||||||
mut start: Option<f32>,
|
mut start: Option<f32>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let Some(SharedBody::Occurrence {
|
||||||
|
body:
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
receiver: Some(receiver),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = p.elements[&call].shared()
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let receiver = *receiver;
|
||||||
let entered_open = start.is_some();
|
let entered_open = start.is_some();
|
||||||
let mut split = false;
|
let mut split = false;
|
||||||
for id in p.elements[&owner].steps().into_iter().flatten() {
|
for id in p.elements[&owner].steps().into_iter().flatten() {
|
||||||
@@ -1571,6 +1704,21 @@ impl Scene {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match p.elements[id].shared() {
|
match p.elements[id].shared() {
|
||||||
|
Some(SharedBody::Occurrence {
|
||||||
|
body:
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
message_kind: MessageKind::Delete,
|
||||||
|
receiver: Some(deleted),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) if *deleted == receiver => {
|
||||||
|
// This path cannot show activation beyond its local death.
|
||||||
|
// Keep the unresolved-call cue; a Delete is not a Reply.
|
||||||
|
self.activation_segment(call, owner, receiver, x, top, y);
|
||||||
|
self.open_calls.push(Point::new(x + 12., y - 16.));
|
||||||
|
start = None;
|
||||||
|
}
|
||||||
Some(SharedBody::Occurrence {
|
Some(SharedBody::Occurrence {
|
||||||
body:
|
body:
|
||||||
OccurrenceBody::Exchange {
|
OccurrenceBody::Exchange {
|
||||||
@@ -1580,14 +1728,19 @@ impl Scene {
|
|||||||
},
|
},
|
||||||
..
|
..
|
||||||
}) if *correlation == call => {
|
}) if *correlation == call => {
|
||||||
self.activation_segment(call, x, top, y);
|
self.activation_segment(call, owner, receiver, x, top, y);
|
||||||
self.boxes.push(BoxItem {
|
for span in self.lifecycle.alive_spans(receiver, owner, y - 1., y + 1.) {
|
||||||
id: call,
|
self.boxes.push(BoxItem {
|
||||||
rect: Rectangle::new(Point::new(x - 7., y - 1.), Size::new(14., 2.)),
|
id: call,
|
||||||
title: String::new(),
|
rect: Rectangle::new(
|
||||||
detail: String::new(),
|
Point::new(x - 7., span.start),
|
||||||
marker: None,
|
Size::new(14., span.end - span.start),
|
||||||
});
|
),
|
||||||
|
title: String::new(),
|
||||||
|
detail: String::new(),
|
||||||
|
marker: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
start = None;
|
start = None;
|
||||||
}
|
}
|
||||||
Some(SharedBody::Occurrence {
|
Some(SharedBody::Occurrence {
|
||||||
@@ -1595,7 +1748,7 @@ impl Scene {
|
|||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
split = true;
|
split = true;
|
||||||
self.activation_segment(call, x, top, y);
|
self.activation_segment(call, owner, receiver, x, top, y);
|
||||||
let bottom = self
|
let bottom = self
|
||||||
.boxes
|
.boxes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1630,7 +1783,7 @@ impl Scene {
|
|||||||
} else {
|
} else {
|
||||||
top + 26.
|
top + 26.
|
||||||
};
|
};
|
||||||
self.activation_segment(call, x, top, bottom);
|
self.activation_segment(call, owner, receiver, x, top, bottom);
|
||||||
self.open_calls.push(Point::new(x + 8., bottom - 7.));
|
self.open_calls.push(Point::new(x + 8., bottom - 7.));
|
||||||
}
|
}
|
||||||
start.is_some()
|
start.is_some()
|
||||||
@@ -1663,6 +1816,12 @@ impl Scene {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
fn hit(&self, point: Point) -> Option<Id> {
|
fn hit(&self, point: Point) -> Option<Id> {
|
||||||
|
if !point.x.is_finite() || !point.y.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(id) = self.lifecycle.marker_at(point) {
|
||||||
|
return Some(id);
|
||||||
|
}
|
||||||
self.boxes
|
self.boxes
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@@ -1677,7 +1836,7 @@ impl Scene {
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|line| {
|
.find(|line| {
|
||||||
!line.label.is_empty()
|
!line.label.is_empty()
|
||||||
&& (Rectangle::new(
|
&& Rectangle::new(
|
||||||
line.label_at,
|
line.label_at,
|
||||||
Size::new(
|
Size::new(
|
||||||
(line.label.chars().count() as f32 * 6.8 + 8.).min(225.),
|
(line.label.chars().count() as f32 * 6.8 + 8.).min(225.),
|
||||||
@@ -1685,13 +1844,10 @@ impl Scene {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.contains(point)
|
.contains(point)
|
||||||
|| line.points.windows(2).any(|p| {
|
|
||||||
crate::diagram::distance_to_segment(point, p[0], p[1])
|
|
||||||
< 8. / self.scale
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.map(|line| line.id)
|
.map(|line| line.id)
|
||||||
})
|
})
|
||||||
|
.or_else(|| self.near_sequence_item(point))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.boxes
|
self.boxes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1699,6 +1855,7 @@ impl Scene {
|
|||||||
.find(|b| b.rect.contains(point) && b.title.is_empty())
|
.find(|b| b.rect.contains(point) && b.title.is_empty())
|
||||||
.map(|b| b.id)
|
.map(|b| b.id)
|
||||||
})
|
})
|
||||||
|
.or_else(|| self.lifecycle.lane_at(point, self.scale))
|
||||||
.or_else(|| self.lane_at(point))
|
.or_else(|| self.lane_at(point))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.lines
|
self.lines
|
||||||
@@ -1713,6 +1870,34 @@ impl Scene {
|
|||||||
.map(|line| line.id)
|
.map(|line| line.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn near_sequence_item(&self, point: Point) -> Option<Id> {
|
||||||
|
if !self.scale.is_finite() || self.scale <= 0. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let messages = self
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.filter(|line| !line.label.is_empty())
|
||||||
|
.map(|line| {
|
||||||
|
let distance = line
|
||||||
|
.points
|
||||||
|
.windows(2)
|
||||||
|
.map(|p| crate::diagram::distance_to_segment(point, p[0], p[1]))
|
||||||
|
.fold(f32::INFINITY, f32::min);
|
||||||
|
(line.id, distance)
|
||||||
|
});
|
||||||
|
// One distance comparison for both kinds of screen-sized hit tolerance.
|
||||||
|
// Dense minimum-Fit rows must not give the first message or last marker
|
||||||
|
// a larger hit priority than the visibly nearer item. UUID breaks ties.
|
||||||
|
self.lifecycle
|
||||||
|
.nearest_marker(point)
|
||||||
|
.into_iter()
|
||||||
|
.chain(messages)
|
||||||
|
.filter(|(_, distance)| *distance <= 8. / self.scale)
|
||||||
|
.min_by(|a, b| a.1.total_cmp(&b.1).then_with(|| a.0.cmp(&b.0)))
|
||||||
|
.map(|(id, _)| id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl canvas::Program<Message> for Scene {
|
impl canvas::Program<Message> for Scene {
|
||||||
type State = SequenceInput;
|
type State = SequenceInput;
|
||||||
@@ -1953,6 +2138,7 @@ impl canvas::Program<Message> for Scene {
|
|||||||
let mut f = Frame::new(renderer, bounds.size());
|
let mut f = Frame::new(renderer, bounds.size());
|
||||||
f.fill_rectangle(Point::ORIGIN, bounds.size(), Color::WHITE);
|
f.fill_rectangle(Point::ORIGIN, bounds.size(), Color::WHITE);
|
||||||
f.scale(self.scale);
|
f.scale(self.scale);
|
||||||
|
self.lifecycle.draw_lanes(&mut f, self.selected);
|
||||||
for b in &self.boxes {
|
for b in &self.boxes {
|
||||||
let outline = matches!(
|
let outline = matches!(
|
||||||
b.marker,
|
b.marker,
|
||||||
@@ -2031,24 +2217,31 @@ impl canvas::Program<Message> for Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for b in &self.boxes {
|
for b in &self.boxes {
|
||||||
f.fill_text(Text {
|
if let Some(clip) = box_text_clip(b.rect, self.scale, f.size()) {
|
||||||
content: short(&b.title, ((b.rect.width - 25.) / 7.) as usize),
|
// Clipped child frames start with an identity transform. Match
|
||||||
position: Point::new(b.rect.x + 12., b.rect.y + 12.),
|
// the card's scale explicitly, as for lifecycle Create labels.
|
||||||
size: 13.into(),
|
f.with_clip(clip, |label| {
|
||||||
color: if b.id == self.selected {
|
label.scale(self.scale);
|
||||||
style::TEAL
|
label.fill_text(Text {
|
||||||
} else {
|
content: short(&b.title, ((b.rect.width - 25.) / 7.) as usize),
|
||||||
style::INK
|
position: Point::new(b.rect.x + 12., b.rect.y + 12.),
|
||||||
},
|
size: 13.into(),
|
||||||
..Default::default()
|
color: if b.id == self.selected {
|
||||||
});
|
style::TEAL
|
||||||
if !b.detail.is_empty() {
|
} else {
|
||||||
f.fill_text(Text {
|
style::INK
|
||||||
content: short(&b.detail, ((b.rect.width - 25.) / 6.) as usize),
|
},
|
||||||
position: Point::new(b.rect.x + 12., b.rect.y + 39.),
|
..Default::default()
|
||||||
size: 11.into(),
|
});
|
||||||
color: style::MUTED,
|
if !b.detail.is_empty() {
|
||||||
..Default::default()
|
label.fill_text(Text {
|
||||||
|
content: short(&b.detail, ((b.rect.width - 25.) / 6.) as usize),
|
||||||
|
position: Point::new(b.rect.x + 12., b.rect.y + 39.),
|
||||||
|
size: 11.into(),
|
||||||
|
color: style::MUTED,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2061,6 +2254,8 @@ impl canvas::Program<Message> for Scene {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
self.lifecycle
|
||||||
|
.draw_markers(&mut f, self.selected, self.scale);
|
||||||
if let Some((context, tool, editable)) = self.interaction {
|
if let Some((context, tool, editable)) = self.interaction {
|
||||||
let active = editable && state.context == Some(context) && state.tool == tool;
|
let active = editable && state.context == Some(context) && state.tool == tool;
|
||||||
let point = cursor
|
let point = cursor
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ impl Picker {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
Source(super::source_draft::Event),
|
||||||
Open(Layer),
|
Open(Layer),
|
||||||
Choose(Family),
|
Choose(Family),
|
||||||
Name(String),
|
Name(String),
|
||||||
@@ -97,6 +98,11 @@ struct Browser {
|
|||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Draft {
|
struct Draft {
|
||||||
|
#[serde(default)]
|
||||||
|
source: super::source_draft::State,
|
||||||
|
#[serde(default = "Id::new_v4")]
|
||||||
|
invocation: Id,
|
||||||
|
|
||||||
version: u32,
|
version: u32,
|
||||||
document: Id,
|
document: Id,
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
@@ -125,6 +131,17 @@ pub struct Created {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
pub fn source_active(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_open(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||||
|
}
|
||||||
|
pub fn source_back(&mut self) -> bool {
|
||||||
|
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_pending(&self) -> bool {
|
pub fn has_pending(&self) -> bool {
|
||||||
self.draft.is_some()
|
self.draft.is_some()
|
||||||
}
|
}
|
||||||
@@ -174,6 +191,49 @@ impl State {
|
|||||||
.command(p)
|
.command(p)
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||||
|
if let Event::Source(event) = event {
|
||||||
|
if self
|
||||||
|
.draft
|
||||||
|
.as_mut()
|
||||||
|
.ok_or("Start the consumer form first.")?
|
||||||
|
.source
|
||||||
|
.dismiss(&event)?
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let d = self
|
||||||
|
.draft
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("Start the consumer form first.")?;
|
||||||
|
if d.family != Some(Family::Machine) {
|
||||||
|
return Err("This reference does not support staged source creation.".into());
|
||||||
|
}
|
||||||
|
if d.document != p.id {
|
||||||
|
return Err("This behavior draft belongs to another document.".into());
|
||||||
|
}
|
||||||
|
let mut next = d.clone();
|
||||||
|
if let Some(id) = next.source.update(
|
||||||
|
p,
|
||||||
|
super::source_draft::Scope::Machine { layer: d.layer },
|
||||||
|
d.invocation,
|
||||||
|
event,
|
||||||
|
)? {
|
||||||
|
next.owner = Some(id);
|
||||||
|
let preview = next
|
||||||
|
.source
|
||||||
|
.preview(p, next.owner)?
|
||||||
|
.ok_or("New owner preview is missing.")?;
|
||||||
|
if !eligible(&preview, next.layer, Picker::Owner, id) {
|
||||||
|
return Err("This participant cannot own the machine.".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.draft = Some(next);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||||
|
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||||
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Cancel => {
|
Event::Cancel => {
|
||||||
self.draft = None;
|
self.draft = None;
|
||||||
@@ -184,6 +244,8 @@ impl State {
|
|||||||
return Err("Create or cancel the pending behavior first.".into());
|
return Err("Create or cancel the pending behavior first.".into());
|
||||||
}
|
}
|
||||||
self.draft = Some(Draft {
|
self.draft = Some(Draft {
|
||||||
|
source: Default::default(),
|
||||||
|
invocation: Id::new_v4(),
|
||||||
version: 1,
|
version: 1,
|
||||||
document: p.id,
|
document: p.id,
|
||||||
layer,
|
layer,
|
||||||
@@ -332,7 +394,7 @@ fn reference_context(p: &Project, e: &Element) -> String {
|
|||||||
};
|
};
|
||||||
format!("{} · {} · {location}", e.kind(), e.alias)
|
format!("{} · {} · {location}", e.kind(), e.alias)
|
||||||
}
|
}
|
||||||
fn reference_label(p: &Project, id: Id) -> String {
|
pub(super) fn reference_label(p: &Project, id: Id) -> String {
|
||||||
p.elements
|
p.elements
|
||||||
.get(&id)
|
.get(&id)
|
||||||
.map(|e| format!("{} · {}", e.name, reference_context(p, e)))
|
.map(|e| format!("{} · {}", e.name, reference_context(p, e)))
|
||||||
@@ -340,6 +402,15 @@ fn reference_label(p: &Project, id: Id) -> String {
|
|||||||
}
|
}
|
||||||
impl Draft {
|
impl Draft {
|
||||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||||
|
self.source.check_scope(
|
||||||
|
super::source_draft::Scope::Machine { layer: self.layer },
|
||||||
|
self.owner,
|
||||||
|
)?;
|
||||||
|
let preview = self.source.preview(p, self.owner)?;
|
||||||
|
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||||
|
self.source.combine(p, self.owner, command)
|
||||||
|
}
|
||||||
|
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||||
if self.document != p.id {
|
if self.document != p.id {
|
||||||
return Err("This draft belongs to another document.".into());
|
return Err("This draft belongs to another document.".into());
|
||||||
}
|
}
|
||||||
@@ -475,6 +546,21 @@ impl State {
|
|||||||
p: &'a Project,
|
p: &'a Project,
|
||||||
) -> Option<UiElement<'a, Event, iced::Theme, R>> {
|
) -> Option<UiElement<'a, Event, iced::Theme, R>> {
|
||||||
let d = self.draft.as_ref()?;
|
let d = self.draft.as_ref()?;
|
||||||
|
if let Some(view) = d.source.view(p) {
|
||||||
|
return Some(
|
||||||
|
column![
|
||||||
|
text(format!(
|
||||||
|
"State machine name: {}",
|
||||||
|
crate::views::short(&d.name, 60)
|
||||||
|
))
|
||||||
|
.size(12),
|
||||||
|
view.map(Event::Source)
|
||||||
|
]
|
||||||
|
.spacing(6)
|
||||||
|
.height(Fill)
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
let mut fields = column![].spacing(10);
|
let mut fields = column![].spacing(10);
|
||||||
let title = if let Some(family) = d.family {
|
let title = if let Some(family) = d.family {
|
||||||
fields = fields.push(
|
fields = fields.push(
|
||||||
@@ -488,7 +574,7 @@ impl State {
|
|||||||
Family::Sequence => {
|
Family::Sequence => {
|
||||||
fields = fields.push(
|
fields = fields.push(
|
||||||
text(format!(
|
text(format!(
|
||||||
"Owned by the {} layer root. Involve existing work after creation.",
|
"Owned by the {} layer root. Add work and exchange contributions after creation.",
|
||||||
d.layer
|
d.layer
|
||||||
))
|
))
|
||||||
.size(12),
|
.size(12),
|
||||||
@@ -531,7 +617,17 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Family::Machine => {
|
Family::Machine => {
|
||||||
|
if let Some(label) = d.source.label(d.owner) {
|
||||||
|
fields = fields.push(text(label).size(13)).push(text("Create and open commits the new owner and machine together, including the first view only when selected.").size(12));
|
||||||
|
}
|
||||||
fields = fields.push(picker_view(p, d, Picker::Owner));
|
fields = fields.push(picker_view(p, d, Picker::Owner));
|
||||||
|
fields = fields.push(
|
||||||
|
button("Create new owner…")
|
||||||
|
.on_press(Event::Source(super::source_draft::Event::Open(
|
||||||
|
d.invocation,
|
||||||
|
)))
|
||||||
|
.padding(7),
|
||||||
|
);
|
||||||
fields = fields.push(
|
fields = fields.push(
|
||||||
checkbox(d.first_view)
|
checkbox(d.first_view)
|
||||||
.label("Create the first state view")
|
.label("Create the first state view")
|
||||||
@@ -544,9 +640,9 @@ impl State {
|
|||||||
} else {
|
} else {
|
||||||
for family in [Family::Sequence, Family::Scenario, Family::Machine] {
|
for family in [Family::Sequence, Family::Scenario, Family::Machine] {
|
||||||
let purpose = match family {
|
let purpose = match family {
|
||||||
Family::Sequence => "A path through existing work and exchanges",
|
Family::Sequence => "A path through work and exchanges",
|
||||||
Family::Scenario => "Ordered interactions from an explicit viewpoint",
|
Family::Scenario => "Ordered interactions from an explicit viewpoint",
|
||||||
Family::Machine => "States and modes of an existing participant",
|
Family::Machine => "States and modes of an explicitly chosen participant",
|
||||||
};
|
};
|
||||||
fields = fields.push(
|
fields = fields.push(
|
||||||
button(
|
button(
|
||||||
@@ -598,7 +694,9 @@ fn picker_view<'a, R: iced::advanced::text::Renderer + 'a>(
|
|||||||
Picker::Owner => d.owner,
|
Picker::Owner => d.owner,
|
||||||
Picker::Context => d.context,
|
Picker::Context => d.context,
|
||||||
};
|
};
|
||||||
|
let staged_owner = picker == Picker::Owner && d.source.label(selected).is_some();
|
||||||
let title = match picker {
|
let title = match picker {
|
||||||
|
Picker::Owner if staged_owner => "Or choose an existing participant",
|
||||||
Picker::Owner => "Owned by participant",
|
Picker::Owner => "Owned by participant",
|
||||||
Picker::Context => "Scenario context · optional",
|
Picker::Context => "Scenario context · optional",
|
||||||
};
|
};
|
||||||
@@ -609,14 +707,17 @@ fn picker_view<'a, R: iced::advanced::text::Renderer + 'a>(
|
|||||||
.padding(6)
|
.padding(6)
|
||||||
]
|
]
|
||||||
.spacing(5);
|
.spacing(5);
|
||||||
if let Some(id) = selected {
|
if let Some(id) = selected
|
||||||
|
&& !staged_owner
|
||||||
|
{
|
||||||
out = out.push(text(format!("Selected: {}", reference_label(p, id))).size(12));
|
out = out.push(text(format!("Selected: {}", reference_label(p, id))).size(12));
|
||||||
} else if picker == Picker::Context {
|
} else if picker == Picker::Context {
|
||||||
out = out.push(text("No context selected").size(12));
|
out = out.push(text("No context selected").size(12));
|
||||||
}
|
}
|
||||||
if options.is_empty() {
|
if options.is_empty() {
|
||||||
out = out.push(text(match picker {
|
out = out.push(text(match picker {
|
||||||
Picker::Owner => "No matching participant. Refine the search, or cancel and create a participant in Architecture first.",
|
Picker::Owner if staged_owner => "No existing participants match this search. The new owner above remains selected.",
|
||||||
|
Picker::Owner => "No matching participant. Refine the search or choose Create new owner.",
|
||||||
Picker::Context => "No matching context. Refine the search, or leave this optional reference empty.",
|
Picker::Context => "No matching context. Refine the search, or leave this optional reference empty.",
|
||||||
}).size(12));
|
}).size(12));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ impl std::fmt::Display for CanvasTool {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum CanvasEvent {
|
pub enum CanvasEvent {
|
||||||
|
First(Context, Tool),
|
||||||
Scrolled(Context, ScrollArea, iced::Vector),
|
Scrolled(Context, ScrollArea, iced::Vector),
|
||||||
Restored(Context, Id, ScrollArea, iced::Vector),
|
Restored(Context, Id, ScrollArea, iced::Vector),
|
||||||
Viewport(Context, Size),
|
Viewport(Context, Size),
|
||||||
@@ -132,7 +133,8 @@ pub enum ScrollArea {
|
|||||||
impl CanvasEvent {
|
impl CanvasEvent {
|
||||||
pub fn context(&self) -> Context {
|
pub fn context(&self) -> Context {
|
||||||
match self {
|
match self {
|
||||||
Self::Scrolled(context, _, _)
|
Self::First(context, _)
|
||||||
|
| Self::Scrolled(context, _, _)
|
||||||
| Self::Restored(context, ..)
|
| Self::Restored(context, ..)
|
||||||
| Self::Viewport(context, _)
|
| Self::Viewport(context, _)
|
||||||
| Self::Zoom(context, _)
|
| Self::Zoom(context, _)
|
||||||
@@ -162,6 +164,7 @@ impl std::fmt::Display for Operator {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
Source(super::source_draft::Event),
|
||||||
Start(Id, Tool),
|
Start(Id, Tool),
|
||||||
Cancel,
|
Cancel,
|
||||||
Submit,
|
Submit,
|
||||||
@@ -240,6 +243,13 @@ struct CallChoiceContext {
|
|||||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Draft {
|
struct Draft {
|
||||||
|
#[serde(default)]
|
||||||
|
source: super::source_draft::State,
|
||||||
|
#[serde(default = "Id::new_v4")]
|
||||||
|
invocation: Id,
|
||||||
|
#[serde(default)]
|
||||||
|
source_origin: Option<(Id, oa_core::Element)>,
|
||||||
|
|
||||||
owner: Id,
|
owner: Id,
|
||||||
parent: Id,
|
parent: Id,
|
||||||
before: Option<Id>,
|
before: Option<Id>,
|
||||||
@@ -272,6 +282,28 @@ struct Draft {
|
|||||||
choice_page: usize,
|
choice_page: usize,
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
|
pub(super) fn pending_name(&self) -> Option<&str> {
|
||||||
|
self.draft.as_ref().map(|d| d.name.as_str())
|
||||||
|
}
|
||||||
|
pub(super) fn source_view<'a>(&'a self, p: &'a Project) -> Option<iced::Element<'a, Event>> {
|
||||||
|
self.draft
|
||||||
|
.as_ref()?
|
||||||
|
.source
|
||||||
|
.view(p)
|
||||||
|
.map(|v| v.map(Event::Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_active(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_open(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||||
|
}
|
||||||
|
pub fn source_back(&mut self) -> bool {
|
||||||
|
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn enter(&mut self, document: Id, owner: Id) {
|
pub(crate) fn enter(&mut self, document: Id, owner: Id) {
|
||||||
if self.presentation.identity != Some((document, owner)) {
|
if self.presentation.identity != Some((document, owner)) {
|
||||||
self.presentation = workspace::Presentation {
|
self.presentation = workspace::Presentation {
|
||||||
@@ -452,8 +484,16 @@ impl State {
|
|||||||
}
|
}
|
||||||
cached.as_ref().unwrap().1.clone()
|
cached.as_ref().unwrap().1.clone()
|
||||||
}
|
}
|
||||||
pub(crate) fn created_selection(&self, command: &Command) -> Option<Id> {
|
pub(crate) fn created_selection(&self, p: &Project, command: &Command) -> Option<Id> {
|
||||||
let draft = self.draft.as_ref()?;
|
let draft = self.draft.as_ref()?;
|
||||||
|
if self
|
||||||
|
.draft
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|d| d.source.label(d.target).is_some())
|
||||||
|
&& self.prepare_pending(p).ok().as_ref() != Some(command)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
fn created(command: &Command, parent: Id) -> Option<Id> {
|
fn created(command: &Command, parent: Id) -> Option<Id> {
|
||||||
match command {
|
match command {
|
||||||
Command::CreateElement(element) if element.parent == Some(parent) => {
|
Command::CreateElement(element) if element.parent == Some(parent) => {
|
||||||
@@ -568,6 +608,61 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||||
|
if let Event::Source(event) = event {
|
||||||
|
if self
|
||||||
|
.draft
|
||||||
|
.as_mut()
|
||||||
|
.ok_or("Start the consumer form first.")?
|
||||||
|
.source
|
||||||
|
.dismiss(&event)?
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let d = self
|
||||||
|
.draft
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("Start the consumer form first.")?;
|
||||||
|
if d.tool != Tool::Lifeline {
|
||||||
|
return Err("This reference does not support staged source creation.".into());
|
||||||
|
}
|
||||||
|
if d.source_origin.as_ref().is_some_and(|(document, owner)| {
|
||||||
|
*document != p.id || p.elements.get(&d.owner) != Some(owner)
|
||||||
|
}) {
|
||||||
|
return Err("The original consumer changed. Cancel and reopen its form; the current draft is retained.".into());
|
||||||
|
}
|
||||||
|
if d.source_origin.is_none() && d.source.engaged() {
|
||||||
|
return Err("The recovered source draft has no original document binding. Go Back or cancel it.".into());
|
||||||
|
}
|
||||||
|
let mut next = d.clone();
|
||||||
|
if next.source_origin.is_none() && matches!(&event, super::source_draft::Event::Open(_))
|
||||||
|
{
|
||||||
|
next.source_origin = Some((
|
||||||
|
p.id,
|
||||||
|
p.elements
|
||||||
|
.get(&d.owner)
|
||||||
|
.ok_or("The consumer owner no longer exists.")?
|
||||||
|
.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(id) = next.source.update(
|
||||||
|
p,
|
||||||
|
super::source_draft::Scope::Lifeline {
|
||||||
|
owner: d.owner,
|
||||||
|
viewpoint: viewpoint(p, d.owner),
|
||||||
|
},
|
||||||
|
d.invocation,
|
||||||
|
event,
|
||||||
|
)? {
|
||||||
|
next.target = Some(id);
|
||||||
|
next.command(p)?;
|
||||||
|
}
|
||||||
|
self.draft = Some(next);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||||
|
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||||
|
}
|
||||||
|
|
||||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||||
return Err(PENDING_DRAFT.into());
|
return Err(PENDING_DRAFT.into());
|
||||||
}
|
}
|
||||||
@@ -577,6 +672,9 @@ impl State {
|
|||||||
return Err("Choose a scenario.".into());
|
return Err("Choose a scenario.".into());
|
||||||
}
|
}
|
||||||
self.draft = Some(Draft {
|
self.draft = Some(Draft {
|
||||||
|
source: Default::default(),
|
||||||
|
invocation: Id::new_v4(),
|
||||||
|
source_origin: Some((p.id, p.elements[&owner].clone())),
|
||||||
owner,
|
owner,
|
||||||
parent: owner,
|
parent: owner,
|
||||||
before: None,
|
before: None,
|
||||||
@@ -940,7 +1038,14 @@ fn valid_body_choice(p: &Project, owner: Id, body: OccurrenceBody) -> bool {
|
|||||||
execute_batch(p, &[Command::CreateElement(e)]).is_ok()
|
execute_batch(p, &[Command::CreateElement(e)]).is_ok()
|
||||||
}
|
}
|
||||||
fn sources(p: &Project, d: &Draft) -> Vec<Choice> {
|
fn sources(p: &Project, d: &Draft) -> Vec<Choice> {
|
||||||
let layer = p.elements[&d.owner].layer();
|
let Some(owner) = p
|
||||||
|
.elements
|
||||||
|
.get(&d.owner)
|
||||||
|
.filter(|e| e.kind() == Kind::Scenario)
|
||||||
|
else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
let layer = owner.layer();
|
||||||
let mut values: Vec<_> = p
|
let mut values: Vec<_> = p
|
||||||
.elements
|
.elements
|
||||||
.values()
|
.values()
|
||||||
@@ -1256,6 +1361,28 @@ impl Draft {
|
|||||||
e
|
e
|
||||||
}
|
}
|
||||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||||
|
if self.source.engaged()
|
||||||
|
&& self.source_origin.as_ref().is_none_or(|(document, owner)| {
|
||||||
|
*document != p.id || p.elements.get(&self.owner) != Some(owner)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err("The original consumer document or owner changed. Cancel and reopen its form; your draft is retained.".into());
|
||||||
|
}
|
||||||
|
self.source.check_scope(
|
||||||
|
super::source_draft::Scope::Lifeline {
|
||||||
|
owner: self.owner,
|
||||||
|
viewpoint: viewpoint(p, self.owner),
|
||||||
|
},
|
||||||
|
self.target,
|
||||||
|
)?;
|
||||||
|
let preview = self.source.preview(p, self.target)?;
|
||||||
|
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||||
|
self.source.combine(p, self.target, command)
|
||||||
|
}
|
||||||
|
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||||
|
if p.elements.get(&self.owner).map(oa_core::Element::kind) != Some(Kind::Scenario) {
|
||||||
|
return Err("The scenario no longer exists. Cancel or restore its owner.".into());
|
||||||
|
}
|
||||||
if !parents(p, self.owner).iter().any(|c| c.id == self.parent) {
|
if !parents(p, self.owner).iter().any(|c| c.id == self.parent) {
|
||||||
return Err("Choose this scenario or one of its operands as destination.".into());
|
return Err("Choose this scenario or one of its operands as destination.".into());
|
||||||
}
|
}
|
||||||
@@ -1266,6 +1393,8 @@ impl Draft {
|
|||||||
.unwrap_or(self.tool.label());
|
.unwrap_or(self.tool.label());
|
||||||
let name = if self.name.trim().is_empty() {
|
let name = if self.name.trim().is_empty() {
|
||||||
default
|
default
|
||||||
|
} else if self.source.label(self.target).is_some() {
|
||||||
|
self.name.as_str()
|
||||||
} else {
|
} else {
|
||||||
self.name.trim()
|
self.name.trim()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -168,6 +168,11 @@ pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a
|
|||||||
.wrap(),
|
.wrap(),
|
||||||
text(if pending {
|
text(if pending {
|
||||||
"Pending form · the highlighted sequence position is retained".to_owned()
|
"Pending form · the highlighted sequence position is retained".to_owned()
|
||||||
|
} else if !p.children(owner).any(|e| e.kind() == Kind::Lifeline) {
|
||||||
|
"Start with a lifeline representing existing or explicitly new work / participants."
|
||||||
|
.into()
|
||||||
|
} else if !p.children(owner).any(|e| e.kind() == Kind::Occurrence) {
|
||||||
|
"Choose a first message, action or another tool; no occurrence is selected yet.".into()
|
||||||
} else {
|
} else {
|
||||||
let guidance = match tool {
|
let guidance = match tool {
|
||||||
CanvasTool::Message(_) => {
|
CanvasTool::Message(_) => {
|
||||||
@@ -186,6 +191,32 @@ pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a
|
|||||||
.size(11)
|
.size(11)
|
||||||
]
|
]
|
||||||
.spacing(6);
|
.spacing(6);
|
||||||
|
if !pending {
|
||||||
|
if !p.children(owner).any(|e| e.kind() == Kind::Lifeline) {
|
||||||
|
header = header.push(
|
||||||
|
row![
|
||||||
|
text(format!("No lifelines · {:?}", viewpoint(p, owner))).size(12),
|
||||||
|
button("Add first lifeline").on_press_maybe(clean.then_some({
|
||||||
|
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Lifeline))
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
.spacing(8),
|
||||||
|
);
|
||||||
|
} else if !p.children(owner).any(|e| e.kind() == Kind::Occurrence) {
|
||||||
|
header = header.push(
|
||||||
|
row![
|
||||||
|
text("No occurrences yet").size(12),
|
||||||
|
button("Add first message").on_press_maybe(clean.then_some({
|
||||||
|
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Message))
|
||||||
|
})),
|
||||||
|
button("Add first action").on_press_maybe(clean.then_some({
|
||||||
|
Message::ScenarioCanvas(CanvasEvent::First(context, Tool::Action))
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
.spacing(8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
if limited {
|
if limited {
|
||||||
header = header
|
header = header
|
||||||
.push(text("Minimum zoom reached · scroll to see the remaining scenario.").size(11));
|
.push(text("Minimum zoom reached · scroll to see the remaining scenario.").size(11));
|
||||||
@@ -295,272 +326,301 @@ pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.filter(|d| d.owner == owner)
|
.filter(|d| d.owner == owner)
|
||||||
{
|
{
|
||||||
let mut form = column![
|
if let Some(child) = d.source.view(p) {
|
||||||
text(d.tool.label()).size(16),
|
form_panel = column![
|
||||||
text_input("Name (defaults to selected source)", &d.name)
|
text(format!(
|
||||||
.on_input(|s| event(Event::Name(s)))
|
"Consumer name: {}",
|
||||||
.padding(9)
|
crate::views::short(&d.name, 60)
|
||||||
.size(12)
|
))
|
||||||
]
|
.size(12),
|
||||||
.spacing(9);
|
child.map(|e| event(Event::Source(e)))
|
||||||
if d.tool != Tool::Lifeline {
|
]
|
||||||
form = form.push(label(format!(
|
.spacing(6)
|
||||||
"Insert: {}",
|
.height(Fill)
|
||||||
slot_label(
|
.into();
|
||||||
p,
|
} else {
|
||||||
Slot {
|
let mut form = column![
|
||||||
parent: d.parent,
|
text(d.tool.label()).size(16),
|
||||||
before: d.before
|
text_input("Name (defaults to selected source)", &d.name)
|
||||||
}
|
.on_input(|s| event(Event::Name(s)))
|
||||||
)
|
.padding(9)
|
||||||
)));
|
.size(12)
|
||||||
if d.before.is_some() {
|
]
|
||||||
form = form.push(
|
.spacing(9);
|
||||||
button("Append to this sequence instead").on_press(event(Event::AppendInstead)),
|
if d.tool != Tool::Lifeline {
|
||||||
);
|
form = form.push(label(format!(
|
||||||
}
|
"Insert: {}",
|
||||||
form = form.push(picker(
|
slot_label(
|
||||||
parents(p, owner),
|
p,
|
||||||
Some(d.parent),
|
Slot {
|
||||||
"Destination sequence / operand…",
|
parent: d.parent,
|
||||||
Event::Parent,
|
before: d.before
|
||||||
));
|
}
|
||||||
}
|
)
|
||||||
if matches!(
|
)));
|
||||||
d.tool,
|
if d.before.is_some() {
|
||||||
Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
|
form = form.push(
|
||||||
) {
|
button("Append to this sequence instead")
|
||||||
form = form.push(picker(
|
.on_press(event(Event::AppendInstead)),
|
||||||
sources(p, d),
|
);
|
||||||
d.target,
|
}
|
||||||
"Choose a named source…",
|
form = form.push(picker(
|
||||||
Event::Target,
|
parents(p, owner),
|
||||||
));
|
Some(d.parent),
|
||||||
}
|
"Destination sequence / operand…",
|
||||||
if d.tool == Tool::Message {
|
Event::Parent,
|
||||||
form = form.push(
|
|
||||||
pick_list(
|
|
||||||
vec![
|
|
||||||
MessageStyle::Asynchronous,
|
|
||||||
MessageStyle::Call,
|
|
||||||
MessageStyle::CallReply,
|
|
||||||
MessageStyle::Reply,
|
|
||||||
MessageStyle::Create,
|
|
||||||
MessageStyle::Delete,
|
|
||||||
MessageStyle::Timer,
|
|
||||||
],
|
|
||||||
Some(d.style),
|
|
||||||
|style| event(Event::Style(style)),
|
|
||||||
)
|
|
||||||
.width(Fill)
|
|
||||||
.padding(9)
|
|
||||||
.text_size(12),
|
|
||||||
);
|
|
||||||
if d.style == MessageStyle::Reply {
|
|
||||||
form = form.push(bounded_picker(
|
|
||||||
app.behavior.scenario.call_choices(p, context, d),
|
|
||||||
d,
|
|
||||||
d.call,
|
|
||||||
"Choose the earlier call…",
|
|
||||||
Event::Call,
|
|
||||||
));
|
));
|
||||||
if d.call.is_some() {
|
}
|
||||||
form = form.push(label(
|
if matches!(
|
||||||
|
d.tool,
|
||||||
|
Tool::Lifeline | Tool::Reuse | Tool::Action | Tool::Observation
|
||||||
|
) {
|
||||||
|
form = form.push(picker(
|
||||||
|
sources(p, d),
|
||||||
|
d.target,
|
||||||
|
"Choose a named source…",
|
||||||
|
Event::Target,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if d.tool == Tool::Lifeline {
|
||||||
|
if let Some(title) = d.source.label(d.target) {
|
||||||
|
form = form.push(label(title)).push(label(
|
||||||
|
"Create commits one new source and this lifeline together.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if viewpoint(p, owner) != ScenarioViewpoint::MixedLegacy {
|
||||||
|
form = form.push(button("Create new source…").on_press(event(Event::Source(
|
||||||
|
super::super::source_draft::Event::Open(d.invocation),
|
||||||
|
))));
|
||||||
|
} else {
|
||||||
|
form=form.push(label("New source creation requires a strict viewpoint. Existing sources remain available."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.tool == Tool::Message {
|
||||||
|
form = form.push(
|
||||||
|
pick_list(
|
||||||
|
vec![
|
||||||
|
MessageStyle::Asynchronous,
|
||||||
|
MessageStyle::Call,
|
||||||
|
MessageStyle::CallReply,
|
||||||
|
MessageStyle::Reply,
|
||||||
|
MessageStyle::Create,
|
||||||
|
MessageStyle::Delete,
|
||||||
|
MessageStyle::Timer,
|
||||||
|
],
|
||||||
|
Some(d.style),
|
||||||
|
|style| event(Event::Style(style)),
|
||||||
|
)
|
||||||
|
.width(Fill)
|
||||||
|
.padding(9)
|
||||||
|
.text_size(12),
|
||||||
|
);
|
||||||
|
if d.style == MessageStyle::Reply {
|
||||||
|
form = form.push(bounded_picker(
|
||||||
|
app.behavior.scenario.call_choices(p, context, d),
|
||||||
|
d,
|
||||||
|
d.call,
|
||||||
|
"Choose the earlier call…",
|
||||||
|
Event::Call,
|
||||||
|
));
|
||||||
|
if d.call.is_some() {
|
||||||
|
form = form.push(label(
|
||||||
"Reply reuses the call exchange in reverse and retains its correlation.",
|
"Reply reuses the call exchange in reverse and retains its correlation.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
} else if d.style != MessageStyle::Timer {
|
|
||||||
form = form.push(bounded_picker(
|
|
||||||
exchanges(p, d),
|
|
||||||
d,
|
|
||||||
d.exchange,
|
|
||||||
"Choose the exchange…",
|
|
||||||
Event::Exchange,
|
|
||||||
));
|
|
||||||
form = form.push(label(
|
|
||||||
"Apply checks that the message is valid at this insertion position.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
form = form.push(picker(
|
|
||||||
d.line_choices(p, true),
|
|
||||||
d.sender,
|
|
||||||
"Sending lifeline…",
|
|
||||||
Event::Sender,
|
|
||||||
));
|
|
||||||
if d.style != MessageStyle::Timer {
|
|
||||||
form = form.push(picker(
|
|
||||||
d.line_choices(p, false),
|
|
||||||
d.receiver,
|
|
||||||
"Receiving lifeline…",
|
|
||||||
Event::Receiver,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if d.style == MessageStyle::Timer {
|
|
||||||
form = form.push(
|
|
||||||
text_input("Duration / deadline, e.g. 30 s", &d.expression)
|
|
||||||
.on_input(|s| event(Event::Expression(s)))
|
|
||||||
.padding(9)
|
|
||||||
.size(12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if p.layer_of(owner) == Some(Layer::Sa)
|
|
||||||
&& viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
|
|
||||||
&& matches!(
|
|
||||||
d.style,
|
|
||||||
MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
|
|
||||||
)
|
|
||||||
{
|
|
||||||
form = form.push(
|
|
||||||
iced::widget::checkbox(d.operation.is_some())
|
|
||||||
.label("Invoke a specific interface operation")
|
|
||||||
.on_toggle(|enabled| event(Event::OperationIntent(enabled))),
|
|
||||||
);
|
|
||||||
if let Some(operation) = &d.operation {
|
|
||||||
form = form
|
|
||||||
.push(bounded_picker(
|
|
||||||
crate::data_values::operation_choices(p, &d.proposed_message(p)),
|
|
||||||
d,
|
|
||||||
operation.allocation,
|
|
||||||
"Choose the exact interface operation allocation…",
|
|
||||||
Event::Operation,
|
|
||||||
))
|
|
||||||
.push(
|
|
||||||
button("Keep operation unresolved")
|
|
||||||
.on_press(event(Event::ClearOperation)),
|
|
||||||
);
|
|
||||||
if let Some(id) = operation.allocation {
|
|
||||||
form = form.push(label(crate::data_values::operation_identity(p, id)));
|
|
||||||
}
|
}
|
||||||
}
|
} else if d.style != MessageStyle::Timer {
|
||||||
}
|
form = form.push(bounded_picker(
|
||||||
if let Some(description) =
|
exchanges(p, d),
|
||||||
crate::data_values::operation_label(p, &d.proposed_message(p))
|
d,
|
||||||
{
|
d.exchange,
|
||||||
form = form.push(label(description));
|
"Choose the exchange…",
|
||||||
}
|
Event::Exchange,
|
||||||
match d.signature(p) {
|
|
||||||
Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
|
|
||||||
Err(problem) => {
|
|
||||||
form = form.push(label(problem));
|
|
||||||
for (key, expression) in d.authored_values() {
|
|
||||||
form = form
|
|
||||||
.push(
|
|
||||||
text(format!(
|
|
||||||
"Retained value · {} / {}",
|
|
||||||
key.path
|
|
||||||
.iter()
|
|
||||||
.map(|id| p.named(*id))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(" → "),
|
|
||||||
p.named(key.field)
|
|
||||||
))
|
|
||||||
.size(11),
|
|
||||||
)
|
|
||||||
.push(
|
|
||||||
text_input("Value expression", expression)
|
|
||||||
.on_input({
|
|
||||||
let key = key.clone();
|
|
||||||
move |raw| event(Event::Value(key.clone(), raw))
|
|
||||||
})
|
|
||||||
.padding(8)
|
|
||||||
.size(12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {}
|
|
||||||
}
|
|
||||||
if d.style == MessageStyle::CallReply {
|
|
||||||
form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matches!(d.tool, Tool::Action | Tool::Observation) {
|
|
||||||
form = form.push(picker(
|
|
||||||
lines(p, owner),
|
|
||||||
d.sender,
|
|
||||||
"Observed lifeline…",
|
|
||||||
Event::Sender,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if d.tool == Tool::Reuse
|
|
||||||
&& let Some(target) = d.target
|
|
||||||
{
|
|
||||||
for line in lines(p, target) {
|
|
||||||
let referenced = line.id;
|
|
||||||
let compatible: Vec<_> = lines(p, owner)
|
|
||||||
.into_iter()
|
|
||||||
.filter(|c| {
|
|
||||||
compatible_binding(p, referenced, c.id)
|
|
||||||
&& !d
|
|
||||||
.bindings
|
|
||||||
.iter()
|
|
||||||
.any(|(r, l)| *r != referenced && *l == c.id)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
if compatible.is_empty() {
|
|
||||||
form = form.push(label(format!(
|
|
||||||
"Add a compatible local lifeline before binding '{}'.",
|
|
||||||
p.named(referenced)
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
form = form
|
|
||||||
.push(label(format!("Bind {}", p.named(referenced))))
|
|
||||||
.push(picker(
|
|
||||||
compatible,
|
|
||||||
d.bindings.get(&referenced).copied(),
|
|
||||||
"Compatible local lifeline…",
|
|
||||||
move |c| Event::Binding(referenced, c),
|
|
||||||
));
|
));
|
||||||
|
form = form.push(label(
|
||||||
|
"Apply checks that the message is valid at this insertion position.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
form = form.push(picker(
|
||||||
|
d.line_choices(p, true),
|
||||||
|
d.sender,
|
||||||
|
"Sending lifeline…",
|
||||||
|
Event::Sender,
|
||||||
|
));
|
||||||
|
if d.style != MessageStyle::Timer {
|
||||||
|
form = form.push(picker(
|
||||||
|
d.line_choices(p, false),
|
||||||
|
d.receiver,
|
||||||
|
"Receiving lifeline…",
|
||||||
|
Event::Receiver,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if d.style == MessageStyle::Timer {
|
||||||
|
form = form.push(
|
||||||
|
text_input("Duration / deadline, e.g. 30 s", &d.expression)
|
||||||
|
.on_input(|s| event(Event::Expression(s)))
|
||||||
|
.padding(9)
|
||||||
|
.size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if p.layer_of(owner) == Some(Layer::Sa)
|
||||||
|
&& viewpoint(p, owner) == ScenarioViewpoint::BoundaryInterface
|
||||||
|
&& matches!(
|
||||||
|
d.style,
|
||||||
|
MessageStyle::Call | MessageStyle::CallReply | MessageStyle::Asynchronous
|
||||||
|
)
|
||||||
|
{
|
||||||
|
form = form.push(
|
||||||
|
iced::widget::checkbox(d.operation.is_some())
|
||||||
|
.label("Invoke a specific interface operation")
|
||||||
|
.on_toggle(|enabled| event(Event::OperationIntent(enabled))),
|
||||||
|
);
|
||||||
|
if let Some(operation) = &d.operation {
|
||||||
|
form = form
|
||||||
|
.push(bounded_picker(
|
||||||
|
crate::data_values::operation_choices(p, &d.proposed_message(p)),
|
||||||
|
d,
|
||||||
|
operation.allocation,
|
||||||
|
"Choose the exact interface operation allocation…",
|
||||||
|
Event::Operation,
|
||||||
|
))
|
||||||
|
.push(
|
||||||
|
button("Keep operation unresolved")
|
||||||
|
.on_press(event(Event::ClearOperation)),
|
||||||
|
);
|
||||||
|
if let Some(id) = operation.allocation {
|
||||||
|
form = form.push(label(crate::data_values::operation_identity(p, id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(description) =
|
||||||
|
crate::data_values::operation_label(p, &d.proposed_message(p))
|
||||||
|
{
|
||||||
|
form = form.push(label(description));
|
||||||
|
}
|
||||||
|
match d.signature(p) {
|
||||||
|
Ok(Some(exchange)) => form = form.push(value_panel(p, d, &exchange)),
|
||||||
|
Err(problem) => {
|
||||||
|
form = form.push(label(problem));
|
||||||
|
for (key, expression) in d.authored_values() {
|
||||||
|
form = form
|
||||||
|
.push(
|
||||||
|
text(format!(
|
||||||
|
"Retained value · {} / {}",
|
||||||
|
key.path
|
||||||
|
.iter()
|
||||||
|
.map(|id| p.named(*id))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" → "),
|
||||||
|
p.named(key.field)
|
||||||
|
))
|
||||||
|
.size(11),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
text_input("Value expression", expression)
|
||||||
|
.on_input({
|
||||||
|
let key = key.clone();
|
||||||
|
move |raw| event(Event::Value(key.clone(), raw))
|
||||||
|
})
|
||||||
|
.padding(8)
|
||||||
|
.size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {}
|
||||||
|
}
|
||||||
|
if d.style == MessageStyle::CallReply {
|
||||||
|
form=form.push(label("Creates the call and reversed, correlated reply together in one undoable operation."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
form = form.push(label(
|
if matches!(d.tool, Tool::Action | Tool::Observation) {
|
||||||
"Bindings are explicit, one-to-one and refer to existing lifelines.",
|
form = form.push(picker(
|
||||||
));
|
lines(p, owner),
|
||||||
}
|
d.sender,
|
||||||
if d.tool == Tool::Fragment {
|
"Observed lifeline…",
|
||||||
form = form.push(
|
Event::Sender,
|
||||||
pick_list(
|
));
|
||||||
vec![
|
}
|
||||||
Operator(FragmentOperator::Alternative),
|
if d.tool == Tool::Reuse
|
||||||
Operator(FragmentOperator::Parallel),
|
&& let Some(target) = d.target
|
||||||
Operator(FragmentOperator::Loop),
|
{
|
||||||
Operator(FragmentOperator::Optional),
|
for line in lines(p, target) {
|
||||||
],
|
let referenced = line.id;
|
||||||
Some(Operator(d.operator)),
|
let compatible: Vec<_> = lines(p, owner)
|
||||||
|o| event(Event::Operator(o)),
|
.into_iter()
|
||||||
)
|
.filter(|c| {
|
||||||
.padding(9)
|
compatible_binding(p, referenced, c.id)
|
||||||
.text_size(12)
|
&& !d
|
||||||
.width(Fill),
|
.bindings
|
||||||
);
|
.iter()
|
||||||
if d.operator != FragmentOperator::Parallel {
|
.any(|(r, l)| *r != referenced && *l == c.id)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if compatible.is_empty() {
|
||||||
|
form = form.push(label(format!(
|
||||||
|
"Add a compatible local lifeline before binding '{}'.",
|
||||||
|
p.named(referenced)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
form = form
|
||||||
|
.push(label(format!("Bind {}", p.named(referenced))))
|
||||||
|
.push(picker(
|
||||||
|
compatible,
|
||||||
|
d.bindings.get(&referenced).copied(),
|
||||||
|
"Compatible local lifeline…",
|
||||||
|
move |c| Event::Binding(referenced, c),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
form = form.push(label(
|
||||||
|
"Bindings are explicit, one-to-one and refer to existing lifelines.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if d.tool == Tool::Fragment {
|
||||||
form = form.push(
|
form = form.push(
|
||||||
text_input("Condition / loop guard", &d.expression)
|
pick_list(
|
||||||
.on_input(|s| event(Event::Expression(s)))
|
vec![
|
||||||
.padding(9)
|
Operator(FragmentOperator::Alternative),
|
||||||
.size(12),
|
Operator(FragmentOperator::Parallel),
|
||||||
|
Operator(FragmentOperator::Loop),
|
||||||
|
Operator(FragmentOperator::Optional),
|
||||||
|
],
|
||||||
|
Some(Operator(d.operator)),
|
||||||
|
|o| event(Event::Operator(o)),
|
||||||
|
)
|
||||||
|
.padding(9)
|
||||||
|
.text_size(12)
|
||||||
|
.width(Fill),
|
||||||
);
|
);
|
||||||
|
if d.operator != FragmentOperator::Parallel {
|
||||||
|
form = form.push(
|
||||||
|
text_input("Condition / loop guard", &d.expression)
|
||||||
|
.on_input(|s| event(Event::Expression(s)))
|
||||||
|
.padding(9)
|
||||||
|
.size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if d.operator == FragmentOperator::Alternative {
|
||||||
|
form = form.push(
|
||||||
|
text_input("Second alternative guard", &d.second_guard)
|
||||||
|
.on_input(|s| event(Event::SecondGuard(s)))
|
||||||
|
.padding(9)
|
||||||
|
.size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
|
||||||
}
|
}
|
||||||
if d.operator == FragmentOperator::Alternative {
|
let readiness = d.command(p);
|
||||||
form = form.push(
|
if let Err(error) = &readiness {
|
||||||
text_input("Second alternative guard", &d.second_guard)
|
form = form.push(label(error.clone()));
|
||||||
.on_input(|s| event(Event::SecondGuard(s)))
|
|
||||||
.padding(9)
|
|
||||||
.size(12),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
form=form.push(label("Creates the fragment with its operands. Choose an operand as destination when adding messages or actions."));
|
let footer = row![
|
||||||
|
button("Create")
|
||||||
|
.padding(9)
|
||||||
|
.on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
|
||||||
|
action("Cancel", event(Event::Cancel))
|
||||||
|
]
|
||||||
|
.spacing(7);
|
||||||
|
form_panel = editor_panel(footer.into(), form.into());
|
||||||
}
|
}
|
||||||
let readiness = d.command(p);
|
|
||||||
if let Err(error) = &readiness {
|
|
||||||
form = form.push(label(error.clone()));
|
|
||||||
}
|
|
||||||
let footer = row![
|
|
||||||
button("Create")
|
|
||||||
.padding(9)
|
|
||||||
.on_press_maybe(readiness.is_ok().then_some(event(Event::Submit))),
|
|
||||||
action("Cancel", event(Event::Cancel))
|
|
||||||
]
|
|
||||||
.spacing(7);
|
|
||||||
form_panel = editor_panel(footer.into(), form.into());
|
|
||||||
}
|
}
|
||||||
workspace_layout(header.into(), details, canvas, form_panel, pending)
|
workspace_layout(header.into(), details, canvas, form_panel, pending)
|
||||||
}
|
}
|
||||||
@@ -649,7 +709,6 @@ fn canvas_pane<'a, R: iced::advanced::text::Renderer + 'a>(
|
|||||||
.width(Fill)
|
.width(Fill)
|
||||||
.height(Fill),
|
.height(Fill),
|
||||||
))
|
))
|
||||||
// A new owner/document needs its first measurement even at the same size.
|
|
||||||
.key((context.document, context.scenario))
|
.key((context.document, context.scenario))
|
||||||
.on_show(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size)))
|
.on_show(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size)))
|
||||||
.on_resize(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size))),
|
.on_resize(move |size| Message::ScenarioCanvas(CanvasEvent::Viewport(context, size))),
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ fn scenario_workspace_fit_uses_measured_inner_viewport_and_rejects_nonfinite_inp
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scenario_workspace_fit_reobserves_same_sized_owner_and_document_changes() {
|
fn scenario_workspace_same_size_owner_and_document_changes_remeasure_fit_viewport() {
|
||||||
use super::super::direct_tests::{navigation_event, navigation_fixture};
|
use super::super::direct_tests::{navigation_event, navigation_fixture};
|
||||||
let (project, [a, b, _], [reference, _, _]) = navigation_fixture();
|
let (project, [a, b, _], [reference, _, _]) = navigation_fixture();
|
||||||
let mut app = App::with_project(project.clone());
|
let mut app = App::with_project(project.clone());
|
||||||
@@ -358,14 +358,13 @@ fn scenario_workspace_fit_reobserves_same_sized_owner_and_document_changes() {
|
|||||||
);
|
);
|
||||||
let mut tree = widget::Tree::new(view.as_widget());
|
let mut tree = widget::Tree::new(view.as_widget());
|
||||||
redraw(&mut view, &mut tree, size, &mut app);
|
redraw(&mut view, &mut tree, size, &mut app);
|
||||||
let (_, first) = geometry(&mut view, &mut tree, size);
|
let first_viewport = app.behavior.scenario.presentation.viewport.unwrap();
|
||||||
let measured = first.scrolls["scenario-canvas"].0.size();
|
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||||
assert_eq!(app.behavior.scenario.presentation.viewport, Some(measured));
|
assert!(app.behavior.scenario.presentation.scale(graph) < 1.);
|
||||||
|
|
||||||
let accepted = app.document.snapshot();
|
|
||||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
|
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, reference));
|
||||||
assert_eq!(app.scenario_context().unwrap().scenario, b);
|
assert_eq!(app.scenario_context().unwrap().scenario, b);
|
||||||
assert_eq!(app.behavior.scenario.presentation.viewport, None);
|
assert!(app.behavior.scenario.presentation.viewport.is_none());
|
||||||
view = fixture_view(
|
view = fixture_view(
|
||||||
&app.behavior.scenario,
|
&app.behavior.scenario,
|
||||||
app.scenario_context().unwrap(),
|
app.scenario_context().unwrap(),
|
||||||
@@ -374,50 +373,38 @@ fn scenario_workspace_fit_reobserves_same_sized_owner_and_document_changes() {
|
|||||||
);
|
);
|
||||||
tree.diff(view.as_widget());
|
tree.diff(view.as_widget());
|
||||||
redraw(&mut view, &mut tree, size, &mut app);
|
redraw(&mut view, &mut tree, size, &mut app);
|
||||||
let (_, second) = geometry(&mut view, &mut tree, size);
|
assert_eq!(
|
||||||
assert_eq!(second.scrolls["scenario-canvas"].0.size(), measured);
|
app.behavior.scenario.presentation.viewport,
|
||||||
assert_eq!(app.behavior.scenario.presentation.viewport, Some(measured));
|
Some(first_viewport)
|
||||||
|
);
|
||||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||||
let fitted = app.behavior.scenario.presentation.scale(graph);
|
let fitted = app.behavior.scenario.presentation.scale(graph);
|
||||||
assert!(
|
assert!(graph.width * fitted <= first_viewport.width - 16.);
|
||||||
fitted < 1.
|
assert!(graph.height * fitted <= first_viewport.height - 16.);
|
||||||
&& graph.width * fitted <= measured.width - 16.
|
|
||||||
&& graph.height * fitted <= measured.height - 16.
|
let prior_document = app.scenario_context().unwrap().document;
|
||||||
|
app = App::with_project(project);
|
||||||
|
let _ = app.update(Message::Select(b));
|
||||||
|
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||||
|
let context = app.scenario_context().unwrap();
|
||||||
|
assert_ne!(context.document, prior_document);
|
||||||
|
assert_eq!(
|
||||||
|
context.scenario, b,
|
||||||
|
"semantic UUID is retained across documents"
|
||||||
);
|
);
|
||||||
|
assert!(app.behavior.scenario.presentation.viewport.is_none());
|
||||||
|
let accepted = app.document.snapshot();
|
||||||
|
view = fixture_view(&app.behavior.scenario, context, false, 68.);
|
||||||
|
tree.diff(view.as_widget());
|
||||||
|
redraw(&mut view, &mut tree, size, &mut app);
|
||||||
|
assert_eq!(
|
||||||
|
app.behavior.scenario.presentation.viewport,
|
||||||
|
Some(first_viewport)
|
||||||
|
);
|
||||||
|
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||||
|
assert_eq!(app.behavior.scenario.presentation.scale(graph), fitted);
|
||||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||||
assert!(!app.document.can_undo());
|
assert!(!app.document.can_undo());
|
||||||
|
|
||||||
let document = app.scenario_context().unwrap().document;
|
|
||||||
let mut replacement = App::with_project(project);
|
|
||||||
let _ = replacement.update(Message::Select(b));
|
|
||||||
let _ = replacement.update(Message::Page(crate::app::Page::Behavior));
|
|
||||||
assert_ne!(replacement.scenario_context().unwrap().document, document);
|
|
||||||
assert_eq!(replacement.behavior.scenario.presentation.viewport, None);
|
|
||||||
let accepted = replacement.document.snapshot();
|
|
||||||
view = fixture_view(
|
|
||||||
&replacement.behavior.scenario,
|
|
||||||
replacement.scenario_context().unwrap(),
|
|
||||||
false,
|
|
||||||
68.,
|
|
||||||
);
|
|
||||||
tree.diff(view.as_widget());
|
|
||||||
redraw(&mut view, &mut tree, size, &mut replacement);
|
|
||||||
let (_, replaced) = geometry(&mut view, &mut tree, size);
|
|
||||||
assert_eq!(replaced.scrolls["scenario-canvas"].0.size(), measured);
|
|
||||||
assert_eq!(
|
|
||||||
replacement.behavior.scenario.presentation.viewport,
|
|
||||||
Some(measured)
|
|
||||||
);
|
|
||||||
navigation_event(&mut replacement, |ctx| CanvasEvent::Zoom(ctx, None));
|
|
||||||
assert_eq!(
|
|
||||||
replacement.behavior.scenario.presentation.scale(graph),
|
|
||||||
fitted
|
|
||||||
);
|
|
||||||
assert!(std::sync::Arc::ptr_eq(
|
|
||||||
&accepted,
|
|
||||||
&replacement.document.snapshot()
|
|
||||||
));
|
|
||||||
assert!(!replacement.document.can_undo());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -0,0 +1,285 @@
|
|||||||
|
//! Read-only lifecycle geometry. Operand screen order is never execution order.
|
||||||
|
use oa_core::{FragmentOperator, Id, Kind, MessageKind, OccurrenceBody, Project, SharedBody};
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
|
pub(super) mod render;
|
||||||
|
|
||||||
|
/// Maps already recorded by the sequence renderer. Rows carry their owning sequence.
|
||||||
|
pub(crate) struct Geometry<'a> {
|
||||||
|
pub rows: &'a BTreeMap<Id, (Id, f32)>,
|
||||||
|
pub scopes: &'a BTreeMap<Id, (f32, f32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub(crate) struct Span {
|
||||||
|
pub start: f32,
|
||||||
|
pub end: f32,
|
||||||
|
}
|
||||||
|
impl Span {
|
||||||
|
fn valid(self) -> bool {
|
||||||
|
self.start.is_finite() && self.end.is_finite() && self.start <= self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum Life {
|
||||||
|
Alive,
|
||||||
|
Absent,
|
||||||
|
Unresolved,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub(crate) struct Interval {
|
||||||
|
pub scope: Id,
|
||||||
|
pub span: Span,
|
||||||
|
pub life: Life,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum MarkerKind {
|
||||||
|
Birth,
|
||||||
|
Death,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub(crate) struct Marker {
|
||||||
|
pub occurrence: Id,
|
||||||
|
pub scope: Id,
|
||||||
|
pub y: f32,
|
||||||
|
pub kind: MarkerKind,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum UncertaintyReason {
|
||||||
|
Conditional,
|
||||||
|
Repeated,
|
||||||
|
Parallel,
|
||||||
|
MissingGeometry,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) struct Uncertainty {
|
||||||
|
pub scope: Id,
|
||||||
|
pub reason: UncertaintyReason,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub(crate) struct LaneLifetime {
|
||||||
|
pub lifeline: Id,
|
||||||
|
pub intervals: Vec<Interval>,
|
||||||
|
pub markers: Vec<Marker>,
|
||||||
|
pub uncertainties: Vec<Uncertainty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stable lane-ID order; source/model/history and caller-owned geometry are untouched.
|
||||||
|
/// An opaque ScenarioUse is a local use, not a caller birth/death contract.
|
||||||
|
pub(crate) fn project_lifetimes(
|
||||||
|
project: &Project,
|
||||||
|
scenario: Id,
|
||||||
|
geometry: &Geometry<'_>,
|
||||||
|
) -> Vec<LaneLifetime> {
|
||||||
|
if project
|
||||||
|
.elements
|
||||||
|
.get(&scenario)
|
||||||
|
.is_none_or(|e| e.kind() != Kind::Scenario)
|
||||||
|
{
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
project
|
||||||
|
.elements
|
||||||
|
.values()
|
||||||
|
.filter(|e| e.kind() == Kind::Lifeline && e.parent == Some(scenario))
|
||||||
|
.map(|e| {
|
||||||
|
let mut projection = Projection {
|
||||||
|
project,
|
||||||
|
geometry,
|
||||||
|
lane: e.id,
|
||||||
|
visiting: BTreeSet::new(),
|
||||||
|
result: LaneLifetime {
|
||||||
|
lifeline: e.id,
|
||||||
|
intervals: vec![],
|
||||||
|
markers: vec![],
|
||||||
|
uncertainties: vec![],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let initial = if projection.contains(scenario, Some(MarkerKind::Birth)) {
|
||||||
|
Life::Unresolved
|
||||||
|
} else {
|
||||||
|
Life::Alive
|
||||||
|
};
|
||||||
|
projection.sequence(scenario, initial);
|
||||||
|
projection.result
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Projection<'a, 'g> {
|
||||||
|
project: &'a Project,
|
||||||
|
geometry: &'a Geometry<'g>,
|
||||||
|
lane: Id,
|
||||||
|
visiting: BTreeSet<Id>,
|
||||||
|
result: LaneLifetime,
|
||||||
|
}
|
||||||
|
impl Projection<'_, '_> {
|
||||||
|
fn body(&self, id: Id) -> Option<&OccurrenceBody> {
|
||||||
|
match self.project.elements.get(&id)?.shared()? {
|
||||||
|
SharedBody::Occurrence { body, .. } => Some(body),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn event(&self, id: Id) -> Option<MarkerKind> {
|
||||||
|
match self.body(id)? {
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
receiver: Some(line),
|
||||||
|
message_kind,
|
||||||
|
..
|
||||||
|
} if *line == self.lane => match message_kind {
|
||||||
|
MessageKind::Create => Some(MarkerKind::Birth),
|
||||||
|
MessageKind::Delete => Some(MarkerKind::Death),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn contains(&self, scope: Id, kind: Option<MarkerKind>) -> bool {
|
||||||
|
// Local structural walk only: no guards, iteration simulation or reuse expansion.
|
||||||
|
let mut pending = vec![scope];
|
||||||
|
let mut seen = BTreeSet::new();
|
||||||
|
while let Some(id) = pending.pop() {
|
||||||
|
if !seen.insert(id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if self
|
||||||
|
.event(id)
|
||||||
|
.is_some_and(|event| kind.is_none_or(|k| event == k))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if let Some(steps) = self.project.elements.get(&id).and_then(|e| e.steps()) {
|
||||||
|
pending.extend(steps);
|
||||||
|
}
|
||||||
|
if let Some(OccurrenceBody::Fragment { branches, .. }) = self.body(id) {
|
||||||
|
pending.extend(branches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn span(&self, scope: Id) -> Option<Span> {
|
||||||
|
let &(start, end) = self.geometry.scopes.get(&scope)?;
|
||||||
|
let span = Span { start, end };
|
||||||
|
span.valid().then_some(span)
|
||||||
|
}
|
||||||
|
fn uncertain(&mut self, scope: Id, reason: UncertaintyReason) {
|
||||||
|
let value = Uncertainty { scope, reason };
|
||||||
|
if !self.result.uncertainties.contains(&value) {
|
||||||
|
self.result.uncertainties.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn interval(&mut self, scope: Id, start: f32, end: f32, life: Life) {
|
||||||
|
if start < end && start.is_finite() && end.is_finite() {
|
||||||
|
self.result.intervals.push(Interval {
|
||||||
|
scope,
|
||||||
|
span: Span { start, end },
|
||||||
|
life,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn sequence(&mut self, scope: Id, entry: Life) -> Life {
|
||||||
|
let Some(span) = self.span(scope) else {
|
||||||
|
self.uncertain(scope, UncertaintyReason::MissingGeometry);
|
||||||
|
return Life::Unresolved;
|
||||||
|
};
|
||||||
|
if !self.visiting.insert(scope) {
|
||||||
|
self.uncertain(scope, UncertaintyReason::MissingGeometry);
|
||||||
|
return Life::Unresolved;
|
||||||
|
}
|
||||||
|
let steps = self
|
||||||
|
.project
|
||||||
|
.elements
|
||||||
|
.get(&scope)
|
||||||
|
.and_then(|e| e.steps())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
// A creation directly in this sequence fixes its local birth boundary.
|
||||||
|
// A creation only in a nested operand cannot fix its ancestor's entry.
|
||||||
|
let mut life = if steps
|
||||||
|
.iter()
|
||||||
|
.any(|&id| self.event(id) == Some(MarkerKind::Birth))
|
||||||
|
{
|
||||||
|
Life::Absent
|
||||||
|
} else {
|
||||||
|
entry
|
||||||
|
};
|
||||||
|
let mut cursor = span.start;
|
||||||
|
for id in steps {
|
||||||
|
let event = self.event(id);
|
||||||
|
let fragment = match self.body(id) {
|
||||||
|
Some(OccurrenceBody::Fragment { operator, branches }) => {
|
||||||
|
Some((*operator, branches.clone()))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if event.is_none() && fragment.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let y = self
|
||||||
|
.geometry
|
||||||
|
.rows
|
||||||
|
.get(&id)
|
||||||
|
.filter(|(owner, y)| {
|
||||||
|
*owner == scope && y.is_finite() && *y >= cursor && *y <= span.end
|
||||||
|
})
|
||||||
|
.map(|(_, y)| *y);
|
||||||
|
let Some(y) = y else {
|
||||||
|
self.uncertain(id, UncertaintyReason::MissingGeometry);
|
||||||
|
life = Life::Unresolved;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
self.interval(scope, cursor, y, life);
|
||||||
|
cursor = y;
|
||||||
|
if let Some(kind) = event {
|
||||||
|
self.result.markers.push(Marker {
|
||||||
|
occurrence: id,
|
||||||
|
scope,
|
||||||
|
y,
|
||||||
|
kind,
|
||||||
|
});
|
||||||
|
life = match kind {
|
||||||
|
MarkerKind::Birth => Life::Alive,
|
||||||
|
MarkerKind::Death => Life::Absent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if let Some((operator, branches)) = fragment {
|
||||||
|
let entry = life;
|
||||||
|
let mut missing = false;
|
||||||
|
for branch in branches {
|
||||||
|
let bounds = self
|
||||||
|
.span(branch)
|
||||||
|
.filter(|s| s.start >= cursor && s.end <= span.end);
|
||||||
|
if let Some(bounds) = bounds {
|
||||||
|
// Every sibling inherits the same entry, never its displayed predecessor's exit.
|
||||||
|
self.sequence(branch, entry);
|
||||||
|
cursor = bounds.end;
|
||||||
|
} else {
|
||||||
|
self.uncertain(branch, UncertaintyReason::MissingGeometry);
|
||||||
|
missing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.contains(id, None) {
|
||||||
|
let reason = match operator {
|
||||||
|
FragmentOperator::Alternative | FragmentOperator::Optional => {
|
||||||
|
UncertaintyReason::Conditional
|
||||||
|
}
|
||||||
|
FragmentOperator::Loop => UncertaintyReason::Repeated,
|
||||||
|
FragmentOperator::Parallel => UncertaintyReason::Parallel,
|
||||||
|
};
|
||||||
|
self.uncertain(id, reason);
|
||||||
|
life = Life::Unresolved;
|
||||||
|
} else if missing {
|
||||||
|
life = Life::Unresolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.interval(scope, cursor, span.end, life);
|
||||||
|
self.visiting.remove(&scope);
|
||||||
|
life
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "scenario_lifecycle/tests.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
//! Scene bridge for local lifetime geometry; no authoring or model mutations.
|
||||||
|
use super::{Geometry, LaneLifetime, Life, Marker, MarkerKind, Span, project_lifetimes};
|
||||||
|
use crate::{behavior::Line, style, views::short};
|
||||||
|
use iced::{
|
||||||
|
Color, Point, Rectangle, Size,
|
||||||
|
widget::canvas::{self, Frame, Path, Stroke, Text},
|
||||||
|
};
|
||||||
|
use oa_core::{Id, OccurrenceBody, Project, SharedBody};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
struct EventMarker {
|
||||||
|
event: Marker,
|
||||||
|
lifeline: Id,
|
||||||
|
center: Point,
|
||||||
|
capsule: Rectangle,
|
||||||
|
name: String,
|
||||||
|
sender_x: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
|
pub(in crate::behavior) struct Rendering {
|
||||||
|
// Complete rendering positions, including warning-admitted unassigned lanes.
|
||||||
|
// Scene.lanes independently retains the existing authoring eligibility rule.
|
||||||
|
lanes: BTreeMap<Id, f32>,
|
||||||
|
span: Option<Span>,
|
||||||
|
lifetimes: Vec<LaneLifetime>,
|
||||||
|
markers: Vec<EventMarker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rendering {
|
||||||
|
pub(in crate::behavior) fn new(
|
||||||
|
project: &Project,
|
||||||
|
owner: Id,
|
||||||
|
lanes: &BTreeMap<Id, f32>,
|
||||||
|
rows: &BTreeMap<Id, (Id, f32)>,
|
||||||
|
insertion_scopes: &BTreeMap<Id, (f32, f32)>,
|
||||||
|
height: f32,
|
||||||
|
) -> Self {
|
||||||
|
let span = Span {
|
||||||
|
start: 85.,
|
||||||
|
end: height - 20.,
|
||||||
|
};
|
||||||
|
if !span.valid() {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
// Insertion scopes deliberately start later and end earlier. Empty and
|
||||||
|
// Create-only scenarios still have the full visible lifetime extent.
|
||||||
|
let mut scopes = insertion_scopes.clone();
|
||||||
|
for (scope, (start, end)) in &mut scopes {
|
||||||
|
if *scope != owner && *start == *end && start.is_finite() {
|
||||||
|
// An empty operand has 42 px between its insertion axis and
|
||||||
|
// frame end. Match the existing 26 px open-activation allowance.
|
||||||
|
*end = *start + 26.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scopes.insert(owner, (span.start, span.end));
|
||||||
|
let lifetimes = project_lifetimes(
|
||||||
|
project,
|
||||||
|
owner,
|
||||||
|
&Geometry {
|
||||||
|
rows,
|
||||||
|
scopes: &scopes,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let lanes: BTreeMap<_, _> = lanes
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, x)| x.is_finite())
|
||||||
|
.map(|(id, x)| (*id, *x))
|
||||||
|
.collect();
|
||||||
|
let mut markers = Vec::new();
|
||||||
|
for lane in &lifetimes {
|
||||||
|
let Some(&x) = lanes.get(&lane.lifeline) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for event in &lane.markers {
|
||||||
|
let sender_x = project
|
||||||
|
.elements
|
||||||
|
.get(&event.occurrence)
|
||||||
|
.and_then(|e| e.shared())
|
||||||
|
.and_then(|body| match body {
|
||||||
|
SharedBody::Occurrence {
|
||||||
|
body:
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
sender: Some(sender),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => lanes.get(sender).copied(),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
markers.push(EventMarker {
|
||||||
|
event: *event,
|
||||||
|
lifeline: lane.lifeline,
|
||||||
|
center: Point::new(x, event.y),
|
||||||
|
capsule: Rectangle::new(
|
||||||
|
Point::new(x - 70., event.y - 14.),
|
||||||
|
Size::new(140., 28.),
|
||||||
|
),
|
||||||
|
name: project.named(lane.lifeline).to_owned(),
|
||||||
|
sender_x,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
lanes,
|
||||||
|
span: Some(span),
|
||||||
|
lifetimes,
|
||||||
|
markers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn align_messages(&self, lines: &mut [Line]) {
|
||||||
|
for marker in &self.markers {
|
||||||
|
let Some(sender) = marker.sender_x else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(line) = lines
|
||||||
|
.iter_mut()
|
||||||
|
.find(|line| line.id == marker.event.occurrence)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let x = marker.center.x;
|
||||||
|
let y = marker.center.y;
|
||||||
|
let self_message = sender == x;
|
||||||
|
let end_x = match marker.event.kind {
|
||||||
|
MarkerKind::Birth if sender < x => marker.capsule.x,
|
||||||
|
MarkerKind::Birth => marker.capsule.x + marker.capsule.width,
|
||||||
|
MarkerKind::Death => x,
|
||||||
|
};
|
||||||
|
line.points = if self_message {
|
||||||
|
// The receiver is on the recorded occurrence row, including self
|
||||||
|
// lifecycle events. The elbow stays outside the birth capsule.
|
||||||
|
let elbow = x + 112.;
|
||||||
|
line.label_at = Point::new(x + 12., y - 52.);
|
||||||
|
vec![
|
||||||
|
Point::new(x, y - 24.),
|
||||||
|
Point::new(elbow, y - 24.),
|
||||||
|
Point::new(elbow, y),
|
||||||
|
Point::new(end_x, y),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
if marker.event.kind == MarkerKind::Birth {
|
||||||
|
// Keep the label above the capsule, including right-to-left
|
||||||
|
// messages whose existing label starts beside the receiver.
|
||||||
|
line.label_at.y = y - 40.;
|
||||||
|
}
|
||||||
|
vec![Point::new(sender, y), Point::new(end_x, y)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn alive_spans(
|
||||||
|
&self,
|
||||||
|
lifeline: Id,
|
||||||
|
scope: Id,
|
||||||
|
top: f32,
|
||||||
|
bottom: f32,
|
||||||
|
) -> Vec<Span> {
|
||||||
|
if !top.is_finite() || !bottom.is_finite() || bottom <= top {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
self.lifetimes
|
||||||
|
.iter()
|
||||||
|
.find(|lane| lane.lifeline == lifeline)
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|lane| &lane.intervals)
|
||||||
|
.filter(|interval| interval.scope == scope && interval.life == Life::Alive)
|
||||||
|
.filter_map(|interval| {
|
||||||
|
let start = top.max(interval.span.start);
|
||||||
|
let end = bottom.min(interval.span.end);
|
||||||
|
(start < end).then_some(Span { start, end })
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn marker_at(&self, point: Point) -> Option<Id> {
|
||||||
|
self.markers
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|marker| match marker.event.kind {
|
||||||
|
MarkerKind::Birth => marker.capsule.contains(point),
|
||||||
|
MarkerKind::Death => marker_distance(marker, point) <= 1.25,
|
||||||
|
})
|
||||||
|
.map(|marker| marker.event.occurrence)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn nearest_marker(&self, point: Point) -> Option<(Id, f32)> {
|
||||||
|
if !point.x.is_finite() || !point.y.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Exact visible shapes are tested first by Scene. A low-zoom tolerance
|
||||||
|
// must not outrank another marker's actual capsule, a header, or a label.
|
||||||
|
self.markers
|
||||||
|
.iter()
|
||||||
|
.map(|marker| (marker.event.occurrence, marker_distance(marker, point)))
|
||||||
|
.min_by(|a, b| a.1.total_cmp(&b.1).then_with(|| a.0.cmp(&b.0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn lane_at(&self, point: Point, scale: f32) -> Option<Id> {
|
||||||
|
let span = self.span?;
|
||||||
|
if !scale.is_finite() || scale <= 0. || point.y < span.start || point.y > span.end {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.lanes
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, x)| (**x - point.x).abs() < 12. / scale)
|
||||||
|
.min_by(|a, b| (a.1 - point.x).abs().total_cmp(&(b.1 - point.x).abs()))
|
||||||
|
.map(|(id, _)| *id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn draw_lanes(&self, frame: &mut Frame, selected: Id) {
|
||||||
|
let Some(span) = self.span else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for (&id, &x) in &self.lanes {
|
||||||
|
frame.stroke(
|
||||||
|
&Path::line(Point::new(x, span.start), Point::new(x, span.end)),
|
||||||
|
Stroke {
|
||||||
|
line_dash: canvas::LineDash {
|
||||||
|
segments: &[2., 5.],
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
..Stroke::default()
|
||||||
|
.with_width(1.)
|
||||||
|
.with_color(if id == selected {
|
||||||
|
Color::from_rgb8(148, 190, 187)
|
||||||
|
} else {
|
||||||
|
Color::from_rgb8(214, 222, 222)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for lifetime in &self.lifetimes {
|
||||||
|
let Some(&x) = self.lanes.get(&lifetime.lifeline) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for interval in &lifetime.intervals {
|
||||||
|
match interval.life {
|
||||||
|
Life::Alive => frame.stroke(
|
||||||
|
&Path::line(
|
||||||
|
Point::new(x, interval.span.start),
|
||||||
|
Point::new(x, interval.span.end),
|
||||||
|
),
|
||||||
|
Stroke::default().with_width(2.).with_color(
|
||||||
|
if lifetime.lifeline == selected {
|
||||||
|
style::TEAL
|
||||||
|
} else {
|
||||||
|
Color::from_rgb8(91, 113, 113)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Life::Unresolved if interval.span.end - interval.span.start >= 16. => {
|
||||||
|
frame.fill_text(Text {
|
||||||
|
content: "?".into(),
|
||||||
|
position: Point::new(x + 9., interval.span.start + 2.),
|
||||||
|
size: 11.into(),
|
||||||
|
color: style::MUTED,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.lanes.is_empty() {
|
||||||
|
frame.fill_text(Text {
|
||||||
|
content:
|
||||||
|
"Dotted: editing guide · Solid: local lifetime · ?: lifetime depends on path"
|
||||||
|
.into(),
|
||||||
|
position: Point::new(28., span.end + 4.),
|
||||||
|
size: 10.into(),
|
||||||
|
color: style::MUTED,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::behavior) fn draw_markers(&self, frame: &mut Frame, selected: Id, scale: f32) {
|
||||||
|
for marker in &self.markers {
|
||||||
|
let color = if selected == marker.event.occurrence || selected == marker.lifeline {
|
||||||
|
style::TEAL
|
||||||
|
} else {
|
||||||
|
style::INK
|
||||||
|
};
|
||||||
|
match marker.event.kind {
|
||||||
|
MarkerKind::Birth => {
|
||||||
|
let path = Path::rounded_rectangle(
|
||||||
|
marker.capsule.position(),
|
||||||
|
marker.capsule.size(),
|
||||||
|
6.into(),
|
||||||
|
);
|
||||||
|
frame.fill(&path, Color::from_rgb8(243, 248, 248));
|
||||||
|
frame.stroke(
|
||||||
|
&path,
|
||||||
|
Stroke::default().with_color(color).with_width(
|
||||||
|
if selected == marker.event.occurrence {
|
||||||
|
2.5
|
||||||
|
} else {
|
||||||
|
1.5
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let Some(clip) = birth_label_clip(marker.capsule, scale, frame.size()) {
|
||||||
|
// WGPU clipped frames start with an identity transform;
|
||||||
|
// neither the parent's scale nor its clip is inherited.
|
||||||
|
frame.with_clip(clip, |label| {
|
||||||
|
label.scale(scale);
|
||||||
|
label.fill_text(Text {
|
||||||
|
content: short(&marker.name, 18),
|
||||||
|
position: marker.capsule.position() + iced::Vector::new(8., 6.),
|
||||||
|
size: 12.into(),
|
||||||
|
color,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MarkerKind::Death => {
|
||||||
|
for (a, b) in death_strokes(marker.center) {
|
||||||
|
frame.stroke(
|
||||||
|
&Path::line(a, b),
|
||||||
|
Stroke::default().with_color(color).with_width(2.5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn birth_label_clip(capsule: Rectangle, scale: f32, viewport: Size) -> Option<Rectangle> {
|
||||||
|
if !scale.is_finite() || scale <= 0. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Rectangle::new(
|
||||||
|
Point::new((capsule.x + 8.) * scale, (capsule.y + 4.) * scale),
|
||||||
|
Size::new((capsule.width - 16.) * scale, (capsule.height - 8.) * scale),
|
||||||
|
)
|
||||||
|
.intersection(&Rectangle::new(Point::ORIGIN, viewport))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn marker_distance(marker: &EventMarker, point: Point) -> f32 {
|
||||||
|
match marker.event.kind {
|
||||||
|
MarkerKind::Birth => {
|
||||||
|
let dx = (marker.capsule.x - point.x)
|
||||||
|
.max(point.x - marker.capsule.x - marker.capsule.width)
|
||||||
|
.max(0.);
|
||||||
|
let dy = (marker.capsule.y - point.y)
|
||||||
|
.max(point.y - marker.capsule.y - marker.capsule.height)
|
||||||
|
.max(0.);
|
||||||
|
dx.hypot(dy)
|
||||||
|
}
|
||||||
|
MarkerKind::Death => death_strokes(marker.center)
|
||||||
|
.iter()
|
||||||
|
.map(|(a, b)| crate::diagram::distance_to_segment(point, *a, *b))
|
||||||
|
.fold(f32::INFINITY, f32::min),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn death_strokes(center: Point) -> [(Point, Point); 2] {
|
||||||
|
[
|
||||||
|
(
|
||||||
|
center + iced::Vector::new(-7., -7.),
|
||||||
|
center + iced::Vector::new(7., 7.),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
center + iced::Vector::new(-7., 7.),
|
||||||
|
center + iced::Vector::new(7., -7.),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "render_tests.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -0,0 +1,796 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::{behavior::Scene, document::Document};
|
||||||
|
use iced::Size;
|
||||||
|
use oa_core::*;
|
||||||
|
|
||||||
|
struct Fixture {
|
||||||
|
project: Project,
|
||||||
|
owner: Id,
|
||||||
|
sender: Id,
|
||||||
|
receiver: Id,
|
||||||
|
work: Id,
|
||||||
|
exchange: Id,
|
||||||
|
}
|
||||||
|
impl Fixture {
|
||||||
|
fn new(layer: Layer) -> Self {
|
||||||
|
let mut project = Project::empty();
|
||||||
|
let kind = if layer == Layer::Oa {
|
||||||
|
Kind::Activity
|
||||||
|
} else {
|
||||||
|
Kind::Function
|
||||||
|
};
|
||||||
|
let a = project.add_element(kind, layer, "Request", None).unwrap();
|
||||||
|
let work = project.add_element(kind, layer, "Provide", None).unwrap();
|
||||||
|
let exchange = project.relate(
|
||||||
|
if layer == Layer::Oa {
|
||||||
|
RelationKind::Interaction
|
||||||
|
} else {
|
||||||
|
RelationKind::FunctionalExchange
|
||||||
|
},
|
||||||
|
"Service",
|
||||||
|
a,
|
||||||
|
work,
|
||||||
|
);
|
||||||
|
let owner = project
|
||||||
|
.add_element(Kind::Scenario, layer, "Lifecycle", None)
|
||||||
|
.unwrap();
|
||||||
|
let mut lane = |name, represented| {
|
||||||
|
let id = project
|
||||||
|
.add_element(Kind::Lifeline, layer, name, Some(owner))
|
||||||
|
.unwrap();
|
||||||
|
*project.elements.get_mut(&id).unwrap().shared_mut().unwrap() = SharedBody::Lifeline {
|
||||||
|
represented: Some(represented),
|
||||||
|
};
|
||||||
|
id
|
||||||
|
};
|
||||||
|
let sender = lane("Requester", a);
|
||||||
|
let receiver = lane("Provider", work);
|
||||||
|
Self {
|
||||||
|
project,
|
||||||
|
owner,
|
||||||
|
sender,
|
||||||
|
receiver,
|
||||||
|
work,
|
||||||
|
exchange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn occurrence(&mut self, parent: Id, name: &str, body: OccurrenceBody) -> Id {
|
||||||
|
let mut element = self
|
||||||
|
.project
|
||||||
|
.make_element(
|
||||||
|
Kind::Occurrence,
|
||||||
|
self.project.elements[&parent].layer(),
|
||||||
|
name,
|
||||||
|
Some(parent),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let id = element.id;
|
||||||
|
*element.shared_mut().unwrap() = SharedBody::Occurrence {
|
||||||
|
body,
|
||||||
|
precondition: String::new(),
|
||||||
|
postcondition: String::new(),
|
||||||
|
};
|
||||||
|
self.project.elements.insert(id, element);
|
||||||
|
let parent = self.project.elements.get_mut(&parent).unwrap();
|
||||||
|
if let Some(steps) = parent.steps_mut() {
|
||||||
|
steps.push(id);
|
||||||
|
} else if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Fragment { branches, .. },
|
||||||
|
..
|
||||||
|
}) = parent.shared_mut()
|
||||||
|
{
|
||||||
|
branches.push(id);
|
||||||
|
} else {
|
||||||
|
panic!("fixture parent must own ordered occurrences or operands");
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
fn message(&mut self, parent: Id, kind: MessageKind) -> Id {
|
||||||
|
self.occurrence(
|
||||||
|
parent,
|
||||||
|
"Message",
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
exchange: Some(self.exchange),
|
||||||
|
sender: Some(self.sender),
|
||||||
|
receiver: Some(self.receiver),
|
||||||
|
message_kind: kind,
|
||||||
|
operation: None,
|
||||||
|
reply_to: None,
|
||||||
|
values: vec![],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn fragment(&mut self, parent: Id, operator: FragmentOperator) -> Id {
|
||||||
|
self.occurrence(
|
||||||
|
parent,
|
||||||
|
"Paths",
|
||||||
|
OccurrenceBody::Fragment {
|
||||||
|
operator,
|
||||||
|
branches: vec![],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn branch(&mut self, fragment: Id) -> Id {
|
||||||
|
self.occurrence(
|
||||||
|
fragment,
|
||||||
|
"Operand",
|
||||||
|
OccurrenceBody::Branch {
|
||||||
|
guard: "condition".into(),
|
||||||
|
occurrences: vec![],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn document(&self) -> Document {
|
||||||
|
validate_candidate(&self.project).expect("ordinary core admission must succeed");
|
||||||
|
let mut bundle = source_project::format_bundle(&self.project);
|
||||||
|
for text in bundle.files.values_mut() {
|
||||||
|
*text = format!(
|
||||||
|
"// Retain exact authored CRLF and Unicode Ω\r\n{}",
|
||||||
|
text.replace('\n', "\r\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let document =
|
||||||
|
Document::from_bundle(bundle.clone(), source_project::ProjectLimits::default())
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(document.project(), &self.project);
|
||||||
|
assert_eq!(document.bundle(), &bundle);
|
||||||
|
document
|
||||||
|
}
|
||||||
|
fn scene(&self) -> Scene {
|
||||||
|
let document = self.document();
|
||||||
|
let before = document.snapshot();
|
||||||
|
let scene = Scene::sequence(document.project(), self.owner, self.owner);
|
||||||
|
assert_eq!(document.project(), &before.decoded.project);
|
||||||
|
assert_eq!(document.bundle(), &before.bundle);
|
||||||
|
assert_eq!(document.revision(), 0);
|
||||||
|
assert!(!document.can_undo() && !document.can_redo() && !document.dirty());
|
||||||
|
assert_eq!(
|
||||||
|
scene.lifecycle,
|
||||||
|
Scene::sequence(document.project(), self.owner, self.owner).lifecycle
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
scene
|
||||||
|
.lines
|
||||||
|
.iter()
|
||||||
|
.flat_map(|line| &line.points)
|
||||||
|
.all(|p| p.x.is_finite() && p.y.is_finite())
|
||||||
|
);
|
||||||
|
scene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intervals(scene: &Scene, lifeline: Id, scope: Id) -> Vec<(f32, f32, Life)> {
|
||||||
|
scene
|
||||||
|
.lifecycle
|
||||||
|
.lifetimes
|
||||||
|
.iter()
|
||||||
|
.find(|lane| lane.lifeline == lifeline)
|
||||||
|
.unwrap()
|
||||||
|
.intervals
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.scope == scope)
|
||||||
|
.map(|i| (i.span.start, i.span.end, i.life))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_long_header_title_and_detail_keep_names_and_have_bounded_clips() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
for name in [
|
||||||
|
"W".repeat(18),
|
||||||
|
"界".repeat(24),
|
||||||
|
"e\u{301}".repeat(24),
|
||||||
|
"👩\u{200d}🔧".repeat(12),
|
||||||
|
] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
f.project.elements.get_mut(&f.receiver).unwrap().name = name.clone();
|
||||||
|
f.project.elements.get_mut(&f.work).unwrap().name = name.clone();
|
||||||
|
let scene = f.scene();
|
||||||
|
let header = scene.boxes.iter().find(|b| b.id == f.receiver).unwrap();
|
||||||
|
assert_eq!(header.title, format!("Lane · {name}"));
|
||||||
|
assert_eq!(header.detail, name);
|
||||||
|
assert_eq!(header.rect.size(), Size::new(204., 70.));
|
||||||
|
assert_eq!(scene.hit(header.rect.center()), Some(f.receiver));
|
||||||
|
// Scroll offset is handled by the existing outer canvas/scrollable;
|
||||||
|
// these are the local drawing frame's coordinates, not screen input.
|
||||||
|
for scale in [0.05, 0.5, 0.512, 0.64, 0.8, 1., 1.953125, 2.] {
|
||||||
|
let clip =
|
||||||
|
crate::behavior::box_text_clip(header.rect, scale, Size::new(2000., 2000.))
|
||||||
|
.unwrap();
|
||||||
|
let world = Rectangle::new(
|
||||||
|
Point::new(clip.x / scale, clip.y / scale),
|
||||||
|
Size::new(clip.width / scale, clip.height / scale),
|
||||||
|
);
|
||||||
|
assert!((world.x - header.rect.x - 10.).abs() < 0.001);
|
||||||
|
assert!((world.y - header.rect.y - 8.).abs() < 0.001);
|
||||||
|
assert!((world.width - 184.).abs() < 0.001);
|
||||||
|
assert!((world.height - 54.).abs() < 0.001);
|
||||||
|
assert!(header.rect.contains(world.position()));
|
||||||
|
assert!(
|
||||||
|
header
|
||||||
|
.rect
|
||||||
|
.contains(Point::new(world.x + world.width, world.y + world.height,))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn box_text_clip_intersects_parent_viewport_and_rejects_empty_interiors() {
|
||||||
|
use crate::behavior::box_text_clip;
|
||||||
|
let card = Rectangle::new(Point::new(100., 200.), Size::new(204., 70.));
|
||||||
|
assert_eq!(
|
||||||
|
box_text_clip(card, 2., Size::new(300., 500.)),
|
||||||
|
Some(Rectangle::new(Point::new(220., 416.), Size::new(80., 84.)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
box_text_clip(
|
||||||
|
Rectangle::new(Point::new(-20., -10.), card.size()),
|
||||||
|
2.,
|
||||||
|
Size::new(300., 500.),
|
||||||
|
),
|
||||||
|
Some(Rectangle::new(Point::ORIGIN, Size::new(300., 104.)))
|
||||||
|
);
|
||||||
|
for viewport in [Size::new(200., 500.), Size::new(300., 400.), Size::ZERO] {
|
||||||
|
assert_eq!(box_text_clip(card, 2., viewport), None);
|
||||||
|
}
|
||||||
|
for scale in [0., -1., f32::NAN, f32::INFINITY] {
|
||||||
|
assert_eq!(box_text_clip(card, scale, Size::new(1000., 1000.)), None);
|
||||||
|
}
|
||||||
|
for size in [Size::new(20., 70.), Size::new(204., 16.), Size::ZERO] {
|
||||||
|
assert_eq!(
|
||||||
|
box_text_clip(Rectangle::new(Point::ORIGIN, size), 1., card.size()),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_wide_latin_header_exceeds_old_character_budget() {
|
||||||
|
use iced::advanced::text::{self, Paragraph as _};
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
f.project.elements.get_mut(&f.receiver).unwrap().name = "W".repeat(18);
|
||||||
|
f.project.elements.get_mut(&f.work).unwrap().name = "W".repeat(24);
|
||||||
|
let scene = f.scene();
|
||||||
|
let header = scene.boxes.iter().find(|b| b.id == f.receiver).unwrap();
|
||||||
|
for (content, size, divisor) in [(&header.title, 13., 7.), (&header.detail, 11., 6.)] {
|
||||||
|
let label = iced::widget::canvas::Text {
|
||||||
|
content: crate::views::short(content, ((header.rect.width - 25.) / divisor) as usize),
|
||||||
|
size: size.into(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
// Use the same real paragraph/font/shaping defaults as canvas Text.
|
||||||
|
// The native defect survives short(): glyph width, not scalar count,
|
||||||
|
// is what exceeds the card. This is a font witness, not a pixel gate.
|
||||||
|
let paragraph = <iced::Renderer as text::Renderer>::Paragraph::with_text(text::Text {
|
||||||
|
content: label.content.as_str(),
|
||||||
|
bounds: Size::INFINITE,
|
||||||
|
size: label.size,
|
||||||
|
line_height: label.line_height,
|
||||||
|
font: label.font,
|
||||||
|
align_x: label.align_x,
|
||||||
|
align_y: label.align_y,
|
||||||
|
shaping: label.shaping,
|
||||||
|
wrapping: text::Wrapping::default(),
|
||||||
|
});
|
||||||
|
let old_available = header.rect.width - 12.;
|
||||||
|
assert!(
|
||||||
|
paragraph.min_width() > old_available,
|
||||||
|
"wide glyph fixture must overflow the old unbounded card: {} <= {old_available}",
|
||||||
|
paragraph.min_width()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn birth_label_clip_keeps_padded_world_bounds_at_each_scene_scale() {
|
||||||
|
let capsule = Rectangle::new(Point::new(100., 200.), Size::new(140., 28.));
|
||||||
|
for (scale, expected) in [
|
||||||
|
(
|
||||||
|
0.05,
|
||||||
|
Rectangle::new(Point::new(5.4, 10.2), Size::new(6.2, 1.)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0.5,
|
||||||
|
Rectangle::new(Point::new(54., 102.), Size::new(62., 10.)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
1.,
|
||||||
|
Rectangle::new(Point::new(108., 204.), Size::new(124., 20.)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2.,
|
||||||
|
Rectangle::new(Point::new(216., 408.), Size::new(248., 40.)),
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let clip = birth_label_clip(capsule, scale, Size::new(1000., 1000.)).unwrap();
|
||||||
|
for (actual, expected) in [
|
||||||
|
(clip.x, expected.x),
|
||||||
|
(clip.y, expected.y),
|
||||||
|
(clip.width, expected.width),
|
||||||
|
(clip.height, expected.height),
|
||||||
|
] {
|
||||||
|
assert!((actual - expected).abs() < 0.0001);
|
||||||
|
}
|
||||||
|
assert!(capsule.contains(Point::new(clip.x / scale, clip.y / scale)));
|
||||||
|
assert!(capsule.contains(Point::new(
|
||||||
|
(clip.x + clip.width) / scale,
|
||||||
|
(clip.y + clip.height) / scale,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn birth_label_clip_preserves_parent_viewport_bounds() {
|
||||||
|
let capsule = Rectangle::new(Point::new(100., 200.), Size::new(140., 28.));
|
||||||
|
assert_eq!(
|
||||||
|
birth_label_clip(capsule, 2., Size::new(300., 420.)),
|
||||||
|
Some(Rectangle::new(Point::new(216., 408.), Size::new(84., 12.)))
|
||||||
|
);
|
||||||
|
let crossing_origin = Rectangle::new(Point::new(-20., -10.), Size::new(140., 28.));
|
||||||
|
assert_eq!(
|
||||||
|
birth_label_clip(crossing_origin, 2., Size::new(300., 420.)),
|
||||||
|
Some(Rectangle::new(Point::ORIGIN, Size::new(224., 28.)))
|
||||||
|
);
|
||||||
|
for viewport in [Size::new(200., 420.), Size::new(300., 400.), Size::ZERO] {
|
||||||
|
assert_eq!(birth_label_clip(capsule, 2., viewport), None);
|
||||||
|
}
|
||||||
|
for scale in [0., -1., f32::NAN, f32::INFINITY] {
|
||||||
|
assert_eq!(
|
||||||
|
birth_label_clip(capsule, scale, Size::new(1000., 1000.)),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_empty_and_create_only_use_visible_root_extents_not_insertion_bounds() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let empty = f.scene();
|
||||||
|
assert_eq!(empty.sequence_bounds[&f.owner], (106., 106.));
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&empty, f.receiver, f.owner),
|
||||||
|
vec![(85., empty.size.height - 20., Life::Alive)]
|
||||||
|
);
|
||||||
|
assert!(empty.lines.is_empty(), "lane guides are not message lines");
|
||||||
|
let create = f.message(f.owner, MessageKind::Create);
|
||||||
|
let scene = f.scene();
|
||||||
|
let birth = scene.rows[&create].1;
|
||||||
|
assert_eq!(birth, 148.);
|
||||||
|
assert_eq!(scene.sequence_bounds[&f.owner], (106., 198.));
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, f.owner),
|
||||||
|
vec![
|
||||||
|
(85., birth, Life::Absent),
|
||||||
|
(birth, scene.size.height - 20., Life::Alive),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.sender, f.owner),
|
||||||
|
vec![(85., scene.size.height - 20., Life::Alive)]
|
||||||
|
);
|
||||||
|
assert_eq!(scene.slots[0].y, 106.);
|
||||||
|
assert_eq!(
|
||||||
|
scene.lane_at(Point::new(scene.lanes[&f.receiver], 90.)),
|
||||||
|
Some(f.receiver)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_birth_death_markers_align_endpoints_and_win_selection_without_mutating_source() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let create = f.message(f.owner, MessageKind::Create);
|
||||||
|
f.occurrence(
|
||||||
|
f.owner,
|
||||||
|
"Work",
|
||||||
|
OccurrenceBody::Action {
|
||||||
|
action: Some(f.work),
|
||||||
|
lifeline: Some(f.receiver),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let delete = f.message(f.owner, MessageKind::Delete);
|
||||||
|
let mut scene = f.scene();
|
||||||
|
let birth = scene.rows[&create].1;
|
||||||
|
let death = scene.rows[&delete].1;
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, f.owner),
|
||||||
|
vec![
|
||||||
|
(85., birth, Life::Absent),
|
||||||
|
(birth, death, Life::Alive),
|
||||||
|
(death, scene.size.height - 20., Life::Absent),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
let capsule = scene
|
||||||
|
.lifecycle
|
||||||
|
.markers
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.event.occurrence == create)
|
||||||
|
.unwrap()
|
||||||
|
.capsule;
|
||||||
|
let line = scene.lines.iter().find(|l| l.id == create).unwrap();
|
||||||
|
let expected_x = if scene.lanes[&f.sender] < scene.lanes[&f.receiver] {
|
||||||
|
capsule.x
|
||||||
|
} else {
|
||||||
|
capsule.x + capsule.width
|
||||||
|
};
|
||||||
|
assert_eq!(line.points.last(), Some(&Point::new(expected_x, birth)));
|
||||||
|
assert!(
|
||||||
|
line.label_at.y + 22. < capsule.y,
|
||||||
|
"message caption clears birth capsule"
|
||||||
|
);
|
||||||
|
let line = scene.lines.iter().find(|l| l.id == delete).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
line.points.last(),
|
||||||
|
Some(&Point::new(scene.lanes[&f.receiver], death))
|
||||||
|
);
|
||||||
|
for scale in [0.5, 1., 2.] {
|
||||||
|
scene.scale = scale;
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(scene.lanes[&f.receiver], birth)),
|
||||||
|
Some(create)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(scene.lanes[&f.receiver], death)),
|
||||||
|
Some(delete)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(
|
||||||
|
scene.lanes[&f.receiver],
|
||||||
|
scene.size.height - 25.
|
||||||
|
)),
|
||||||
|
Some(f.receiver)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(scene.lanes[&f.receiver], 40.)),
|
||||||
|
Some(f.receiver)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_branch_delete_clips_only_its_activation_and_keeps_unanswered_call_visible() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let call = f.message(f.owner, MessageKind::Call);
|
||||||
|
let fragment = f.fragment(f.owner, FragmentOperator::Alternative);
|
||||||
|
let left = f.branch(fragment);
|
||||||
|
let delete = f.message(left, MessageKind::Delete);
|
||||||
|
let right = f.branch(fragment);
|
||||||
|
let scene = f.scene();
|
||||||
|
let death = scene.rows[&delete].1;
|
||||||
|
let (left_start, left_end) = scene.sequence_bounds[&left];
|
||||||
|
let (right_start, _) = scene.sequence_bounds[&right];
|
||||||
|
let bars: Vec<_> = scene
|
||||||
|
.boxes
|
||||||
|
.iter()
|
||||||
|
.filter(|b| b.id == call && b.rect.width == 8.)
|
||||||
|
.map(|b| b.rect)
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
bars.iter()
|
||||||
|
.any(|r| r.y == left_start && r.y + r.height == death)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!bars
|
||||||
|
.iter()
|
||||||
|
.any(|r| r.y < left_end && r.y + r.height > death && r.y >= left_start)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
bars.iter().any(|r| r.y == right_start && r.height == 26.),
|
||||||
|
"empty exclusive sibling retains its own open activation"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, right),
|
||||||
|
vec![(right_start, right_start + 26., Life::Alive)]
|
||||||
|
);
|
||||||
|
let frame_end = scene.boxes.iter().find(|b| b.id == fragment).unwrap().rect;
|
||||||
|
assert!(
|
||||||
|
!bars.iter().any(|r| r.y >= frame_end.y + frame_end.height),
|
||||||
|
"uncertain post-fragment lifetime cannot carry a definite activation bar"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
scene
|
||||||
|
.open_calls
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.y < death && p.y > left_start)
|
||||||
|
);
|
||||||
|
assert!(scene.open_calls.iter().any(|p| p.y > right_start));
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, left),
|
||||||
|
vec![
|
||||||
|
(left_start, death, Life::Alive),
|
||||||
|
(death, left_end, Life::Absent)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, f.owner).last().unwrap().2,
|
||||||
|
Life::Unresolved
|
||||||
|
);
|
||||||
|
assert!(!f.project.elements.values().any(|e| matches!(
|
||||||
|
e.shared(),
|
||||||
|
Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange {
|
||||||
|
message_kind: MessageKind::Reply,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_unassigned_lanes_and_explicit_incomplete_receiver_keep_real_marker_identity() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
*f.project
|
||||||
|
.elements
|
||||||
|
.get_mut(&f.receiver)
|
||||||
|
.unwrap()
|
||||||
|
.shared_mut()
|
||||||
|
.unwrap() = SharedBody::Lifeline { represented: None };
|
||||||
|
let create = f.message(f.owner, MessageKind::Create);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange { exchange, .. },
|
||||||
|
..
|
||||||
|
}) = f.project.elements.get_mut(&create).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*exchange = None;
|
||||||
|
}
|
||||||
|
let scene = f.scene();
|
||||||
|
assert!(
|
||||||
|
!scene.lanes.contains_key(&f.receiver),
|
||||||
|
"authoring eligibility remains separate"
|
||||||
|
);
|
||||||
|
let x = scene.lifecycle.lanes[&f.receiver];
|
||||||
|
assert_eq!(scene.lifecycle.markers[0].lifeline, f.receiver);
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.rows[&create].1)),
|
||||||
|
Some(create)
|
||||||
|
);
|
||||||
|
assert_eq!(scene.hit(Point::new(x, 40.)), Some(f.receiver));
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.size.height - 25.)),
|
||||||
|
Some(f.receiver)
|
||||||
|
);
|
||||||
|
assert_eq!(scene.lane_at(Point::new(x, 90.)), None);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange {
|
||||||
|
receiver, exchange, ..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = f.project.elements.get_mut(&create).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*receiver = None;
|
||||||
|
*exchange = Some(f.exchange);
|
||||||
|
}
|
||||||
|
let missing = f.scene();
|
||||||
|
assert!(
|
||||||
|
missing.lifecycle.markers.is_empty(),
|
||||||
|
"do not infer receiver from global exchange"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_minimum_zoom_marker_halos_do_not_steal_adjacent_capsule_or_header_hits() {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
*f.project
|
||||||
|
.elements
|
||||||
|
.get_mut(&f.receiver)
|
||||||
|
.unwrap()
|
||||||
|
.shared_mut()
|
||||||
|
.unwrap() = SharedBody::Lifeline { represented: None };
|
||||||
|
let create = f.message(f.owner, MessageKind::Create);
|
||||||
|
let delete = f.message(f.owner, MessageKind::Delete);
|
||||||
|
for event in [create, delete] {
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange { exchange, .. },
|
||||||
|
..
|
||||||
|
}) = f.project.elements.get_mut(&event).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*exchange = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut scene = f.scene();
|
||||||
|
let x = scene.lifecycle.lanes[&f.receiver];
|
||||||
|
assert_eq!(scene.rows[&delete].1 - scene.rows[&create].1, 92.);
|
||||||
|
for scale in [0.05, 0.1, 0.5, 1., 2.] {
|
||||||
|
scene.scale = scale;
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.rows[&create].1)),
|
||||||
|
Some(create)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.rows[&delete].1)),
|
||||||
|
Some(delete)
|
||||||
|
);
|
||||||
|
assert_eq!(scene.hit(Point::new(x, 40.)), Some(f.receiver));
|
||||||
|
if scale <= 0.1 {
|
||||||
|
for offset in [-25., -15., 15., 25.] {
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.rows[&delete].1 + offset)),
|
||||||
|
Some(delete),
|
||||||
|
"a nearby death halo must beat an earlier message's wider tolerance"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_minimum_zoom_near_ordinary_message_wins_over_lifecycle_halos() {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
f.message(f.owner, MessageKind::Create);
|
||||||
|
let ordinary = f.message(f.owner, MessageKind::Asynchronous);
|
||||||
|
f.message(f.owner, MessageKind::Delete);
|
||||||
|
let mut scene = f.scene();
|
||||||
|
let x = (scene.lanes[&f.sender] + scene.lanes[&f.receiver]) / 2.;
|
||||||
|
for scale in [0.05, 0.1] {
|
||||||
|
scene.scale = scale;
|
||||||
|
assert_eq!(
|
||||||
|
scene.hit(Point::new(x, scene.rows[&ordinary].1 + 2.)),
|
||||||
|
Some(ordinary)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_self_lifecycle_elbows_end_on_recorded_marker_rows() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
for kind in [MessageKind::Create, MessageKind::Delete] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let event = f.message(f.owner, kind);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body:
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
exchange, sender, ..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = f.project.elements.get_mut(&event).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*exchange = None;
|
||||||
|
*sender = Some(f.receiver);
|
||||||
|
}
|
||||||
|
let scene = f.scene();
|
||||||
|
let marker = &scene.lifecycle.markers[0];
|
||||||
|
let line = scene.lines.iter().find(|l| l.id == event).unwrap();
|
||||||
|
assert_eq!(line.points.len(), 4);
|
||||||
|
assert_eq!(line.points[0].y, marker.center.y - 24.);
|
||||||
|
assert_eq!(line.points[3].y, marker.center.y);
|
||||||
|
assert_eq!(
|
||||||
|
line.points[3].x,
|
||||||
|
if marker.event.kind == MarkerKind::Birth {
|
||||||
|
marker.capsule.x + marker.capsule.width
|
||||||
|
} else {
|
||||||
|
marker.center.x
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert!(line.points[1].x > marker.capsule.x + marker.capsule.width);
|
||||||
|
assert_eq!(scene.hit(marker.center), Some(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_self_delete_caption_clears_the_preceding_action_box() {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
let action = f.occurrence(
|
||||||
|
f.owner,
|
||||||
|
"Finish work",
|
||||||
|
OccurrenceBody::Action {
|
||||||
|
action: Some(f.work),
|
||||||
|
lifeline: Some(f.receiver),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let delete = f.message(f.owner, MessageKind::Delete);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange {
|
||||||
|
exchange, sender, ..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = f.project.elements.get_mut(&delete).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*exchange = None;
|
||||||
|
*sender = Some(f.receiver);
|
||||||
|
}
|
||||||
|
let scene = f.scene();
|
||||||
|
let action = scene.boxes.iter().find(|b| b.id == action).unwrap();
|
||||||
|
let line = scene.lines.iter().find(|l| l.id == delete).unwrap();
|
||||||
|
assert!(line.label_at.y > action.rect.y + action.rect.height);
|
||||||
|
assert!(line.label_at.y + 22. < line.points[0].y);
|
||||||
|
assert_eq!(line.points[3].y, scene.rows[&delete].1);
|
||||||
|
assert_eq!(
|
||||||
|
scene
|
||||||
|
.slots
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.slot.before == Some(delete))
|
||||||
|
.unwrap()
|
||||||
|
.y,
|
||||||
|
scene.rows[&delete].1 - 42.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_conditional_repeated_and_parallel_scopes_do_not_claim_definite_continuation() {
|
||||||
|
for operator in [
|
||||||
|
FragmentOperator::Alternative,
|
||||||
|
FragmentOperator::Optional,
|
||||||
|
FragmentOperator::Loop,
|
||||||
|
FragmentOperator::Parallel,
|
||||||
|
] {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
let fragment = f.fragment(f.owner, operator);
|
||||||
|
let first = f.branch(fragment);
|
||||||
|
let create = f.message(first, MessageKind::Create);
|
||||||
|
let scene = f.scene();
|
||||||
|
assert_eq!(scene.lifecycle.markers[0].event.occurrence, create);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, f.owner).first().unwrap().2,
|
||||||
|
Life::Unresolved
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&scene, f.receiver, f.owner).last().unwrap().2,
|
||||||
|
Life::Unresolved
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
scene
|
||||||
|
.lifecycle
|
||||||
|
.lifetimes
|
||||||
|
.iter()
|
||||||
|
.find(|l| l.lifeline == f.sender)
|
||||||
|
.unwrap()
|
||||||
|
.intervals
|
||||||
|
.iter()
|
||||||
|
.all(|i| i.life == Life::Alive)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn real_scene_lifecycle_edit_undo_redo_restores_exact_source_and_changed_geometry() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let create = f.message(f.owner, MessageKind::Create);
|
||||||
|
let mut document = f.document();
|
||||||
|
let before = document.snapshot();
|
||||||
|
let geometry = Scene::sequence(document.project(), f.owner, create).lifecycle;
|
||||||
|
let mut edited = document.project().elements[&create].clone();
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange { message_kind, .. },
|
||||||
|
..
|
||||||
|
}) = edited.shared_mut()
|
||||||
|
{
|
||||||
|
*message_kind = MessageKind::Asynchronous;
|
||||||
|
}
|
||||||
|
document
|
||||||
|
.execute(Command::EditElement(create, edited))
|
||||||
|
.unwrap();
|
||||||
|
let after = document.snapshot();
|
||||||
|
let changed = Scene::sequence(document.project(), f.owner, create).lifecycle;
|
||||||
|
assert_ne!(geometry, changed);
|
||||||
|
assert!(changed.markers.is_empty());
|
||||||
|
assert_ne!(before.bundle, after.bundle);
|
||||||
|
assert!(document.undo());
|
||||||
|
assert_eq!(document.project(), &before.decoded.project);
|
||||||
|
assert_eq!(document.bundle(), &before.bundle);
|
||||||
|
assert_eq!(
|
||||||
|
Scene::sequence(document.project(), f.owner, create).lifecycle,
|
||||||
|
geometry
|
||||||
|
);
|
||||||
|
assert!(!document.can_undo() && document.can_redo());
|
||||||
|
assert!(document.redo());
|
||||||
|
assert_eq!(document.project(), &after.decoded.project);
|
||||||
|
assert_eq!(document.bundle(), &after.bundle);
|
||||||
|
assert_eq!(
|
||||||
|
Scene::sequence(document.project(), f.owner, create).lifecycle,
|
||||||
|
changed
|
||||||
|
);
|
||||||
|
assert!(!document.can_redo());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,470 @@
|
|||||||
|
use super::*;
|
||||||
|
use oa_core::*;
|
||||||
|
|
||||||
|
struct Fixture {
|
||||||
|
p: Project,
|
||||||
|
owner: Id,
|
||||||
|
a: Id,
|
||||||
|
b: Id,
|
||||||
|
exchange: Id,
|
||||||
|
rows: BTreeMap<Id, (Id, f32)>,
|
||||||
|
scopes: BTreeMap<Id, (f32, f32)>,
|
||||||
|
}
|
||||||
|
impl Fixture {
|
||||||
|
fn new(layer: Layer) -> Self {
|
||||||
|
let mut p = Project::empty();
|
||||||
|
let kind = if layer == Layer::Oa {
|
||||||
|
Kind::Activity
|
||||||
|
} else {
|
||||||
|
Kind::Function
|
||||||
|
};
|
||||||
|
let a = p.add_element(kind, layer, "Request", None).unwrap();
|
||||||
|
let b = p.add_element(kind, layer, "Provide", None).unwrap();
|
||||||
|
let owner = p
|
||||||
|
.add_element(Kind::Scenario, layer, "Lifecycle", None)
|
||||||
|
.unwrap();
|
||||||
|
let exchange = p.relate(
|
||||||
|
if layer == Layer::Oa {
|
||||||
|
RelationKind::Interaction
|
||||||
|
} else {
|
||||||
|
RelationKind::FunctionalExchange
|
||||||
|
},
|
||||||
|
"Service",
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
);
|
||||||
|
let line = |p: &mut Project, represented| {
|
||||||
|
let id = p
|
||||||
|
.add_element(Kind::Lifeline, layer, "Participant", Some(owner))
|
||||||
|
.unwrap();
|
||||||
|
*p.elements.get_mut(&id).unwrap().shared_mut().unwrap() = SharedBody::Lifeline {
|
||||||
|
represented: Some(represented),
|
||||||
|
};
|
||||||
|
id
|
||||||
|
};
|
||||||
|
let a = line(&mut p, a);
|
||||||
|
let b = line(&mut p, b);
|
||||||
|
Self {
|
||||||
|
p,
|
||||||
|
owner,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
exchange,
|
||||||
|
rows: BTreeMap::new(),
|
||||||
|
scopes: [(owner, (0., 600.))].into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn occurrence(&mut self, owner: Id, y: f32, body: OccurrenceBody) -> Id {
|
||||||
|
let mut e = self
|
||||||
|
.p
|
||||||
|
.make_element(
|
||||||
|
Kind::Occurrence,
|
||||||
|
self.p.elements[&owner].layer(),
|
||||||
|
"Occurrence",
|
||||||
|
Some(owner),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let id = e.id;
|
||||||
|
*e.shared_mut().unwrap() = SharedBody::Occurrence {
|
||||||
|
body,
|
||||||
|
precondition: String::new(),
|
||||||
|
postcondition: String::new(),
|
||||||
|
};
|
||||||
|
self.p.elements.insert(id, e);
|
||||||
|
if let Some(steps) = self.p.elements.get_mut(&owner).unwrap().steps_mut() {
|
||||||
|
steps.push(id);
|
||||||
|
} else if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Fragment { branches, .. },
|
||||||
|
..
|
||||||
|
}) = self.p.elements.get_mut(&owner).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
branches.push(id);
|
||||||
|
}
|
||||||
|
self.rows.insert(id, (owner, y));
|
||||||
|
id
|
||||||
|
}
|
||||||
|
fn message(&mut self, owner: Id, y: f32, kind: MessageKind) -> Id {
|
||||||
|
self.occurrence(
|
||||||
|
owner,
|
||||||
|
y,
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
exchange: Some(self.exchange),
|
||||||
|
sender: Some(self.a),
|
||||||
|
receiver: Some(self.b),
|
||||||
|
message_kind: kind,
|
||||||
|
operation: None,
|
||||||
|
reply_to: None,
|
||||||
|
values: vec![],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn fragment(&mut self, owner: Id, y: f32, operator: FragmentOperator) -> Id {
|
||||||
|
self.occurrence(
|
||||||
|
owner,
|
||||||
|
y,
|
||||||
|
OccurrenceBody::Fragment {
|
||||||
|
operator,
|
||||||
|
branches: vec![],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn branch(&mut self, fragment: Id, start: f32, end: f32) -> Id {
|
||||||
|
let id = self.occurrence(
|
||||||
|
fragment,
|
||||||
|
start,
|
||||||
|
OccurrenceBody::Branch {
|
||||||
|
guard: "condition".into(),
|
||||||
|
occurrences: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.scopes.insert(id, (start, end));
|
||||||
|
id
|
||||||
|
}
|
||||||
|
fn project(&self) -> Vec<LaneLifetime> {
|
||||||
|
let errors: Vec<_> = validation::validate(&self.p)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|i| i.severity == validation::Severity::Error)
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
errors.is_empty(),
|
||||||
|
"fixture must be ordinarily admitted: {errors:?}"
|
||||||
|
);
|
||||||
|
let before = self.p.clone();
|
||||||
|
let source = source_project::format_bundle(&self.p);
|
||||||
|
let geometry = Geometry {
|
||||||
|
rows: &self.rows,
|
||||||
|
scopes: &self.scopes,
|
||||||
|
};
|
||||||
|
let result = project_lifetimes(&self.p, self.owner, &geometry);
|
||||||
|
assert_eq!(result, project_lifetimes(&self.p, self.owner, &geometry));
|
||||||
|
assert_eq!(self.p, before);
|
||||||
|
assert_eq!(source_project::format_bundle(&self.p), source);
|
||||||
|
assert!(
|
||||||
|
result
|
||||||
|
.windows(2)
|
||||||
|
.all(|pair| pair[0].lifeline < pair[1].lifeline)
|
||||||
|
);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
fn lane(&self) -> LaneLifetime {
|
||||||
|
self.project()
|
||||||
|
.into_iter()
|
||||||
|
.find(|lane| lane.lifeline == self.b)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn intervals(lane: &LaneLifetime, scope: Id) -> Vec<(f32, f32, Life)> {
|
||||||
|
lane.intervals
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.scope == scope)
|
||||||
|
.map(|i| (i.span.start, i.span.end, i.life))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn root_lifetime_has_exact_birth_death_boundaries_without_source_mutation() {
|
||||||
|
for layer in [Layer::Oa, Layer::Sa] {
|
||||||
|
let mut f = Fixture::new(layer);
|
||||||
|
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||||
|
f.message(f.owner, 200., MessageKind::Asynchronous);
|
||||||
|
let delete = f.message(f.owner, 300., MessageKind::Delete);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, f.owner),
|
||||||
|
vec![
|
||||||
|
(0., 100., Life::Absent),
|
||||||
|
(100., 300., Life::Alive),
|
||||||
|
(300., 600., Life::Absent)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lane.markers,
|
||||||
|
vec![
|
||||||
|
Marker {
|
||||||
|
occurrence: create,
|
||||||
|
scope: f.owner,
|
||||||
|
y: 100.,
|
||||||
|
kind: MarkerKind::Birth
|
||||||
|
},
|
||||||
|
Marker {
|
||||||
|
occurrence: delete,
|
||||||
|
scope: f.owner,
|
||||||
|
y: 300.,
|
||||||
|
kind: MarkerKind::Death
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
let sender = f
|
||||||
|
.project()
|
||||||
|
.into_iter()
|
||||||
|
.find(|lane| lane.lifeline == f.a)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(intervals(&sender, f.owner), vec![(0., 600., Life::Alive)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alternative_death_does_not_kill_its_displayed_sibling() {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
let fragment = f.fragment(f.owner, 100., FragmentOperator::Alternative);
|
||||||
|
let left = f.branch(fragment, 120., 250.);
|
||||||
|
let right = f.branch(fragment, 300., 450.);
|
||||||
|
f.message(left, 200., MessageKind::Delete);
|
||||||
|
f.message(right, 350., MessageKind::Asynchronous);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, left),
|
||||||
|
vec![(120., 200., Life::Alive), (200., 250., Life::Absent)]
|
||||||
|
);
|
||||||
|
assert_eq!(intervals(&lane, right), vec![(300., 450., Life::Alive)]);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, f.owner),
|
||||||
|
vec![(0., 100., Life::Alive), (450., 600., Life::Unresolved)]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lane.uncertainties,
|
||||||
|
vec![Uncertainty {
|
||||||
|
scope: fragment,
|
||||||
|
reason: UncertaintyReason::Conditional
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn conditional_birth_does_not_infer_absence_in_siblings_or_ancestors() {
|
||||||
|
for operator in [FragmentOperator::Alternative, FragmentOperator::Optional] {
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
let fragment = f.fragment(f.owner, 100., operator);
|
||||||
|
let left = f.branch(fragment, 120., 250.);
|
||||||
|
let right = f.branch(fragment, 300., 450.);
|
||||||
|
f.message(left, 200., MessageKind::Create);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, left),
|
||||||
|
vec![(120., 200., Life::Absent), (200., 250., Life::Alive)]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, right),
|
||||||
|
vec![(300., 450., Life::Unresolved)]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, f.owner),
|
||||||
|
vec![(0., 100., Life::Unresolved), (450., 600., Life::Unresolved)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loop_lifetime_is_local_and_warning_bearing_loops_remain_drawable() {
|
||||||
|
for balanced in [true, false] {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
let fragment = f.fragment(f.owner, 100., FragmentOperator::Loop);
|
||||||
|
let iteration = f.branch(fragment, 120., 450.);
|
||||||
|
let create = f.message(iteration, 160., MessageKind::Create);
|
||||||
|
f.message(iteration, 250., MessageKind::Asynchronous);
|
||||||
|
if balanced {
|
||||||
|
f.message(iteration, 350., MessageKind::Delete);
|
||||||
|
}
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(lane.markers.len(), if balanced { 2 } else { 1 });
|
||||||
|
assert_eq!(lane.markers[0].occurrence, create);
|
||||||
|
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||||
|
scope: fragment,
|
||||||
|
reason: UncertaintyReason::Repeated
|
||||||
|
}));
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, f.owner).last().unwrap().2,
|
||||||
|
Life::Unresolved
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
validation::validate(&f.p)
|
||||||
|
.iter()
|
||||||
|
.any(|i| i.code == "lifeline_repetition"),
|
||||||
|
!balanced
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_cleanup_inside_loop_does_not_claim_balanced_or_definite_exit() {
|
||||||
|
let mut f = Fixture::new(Layer::Sa);
|
||||||
|
let repeated = f.fragment(f.owner, 100., FragmentOperator::Loop);
|
||||||
|
let iteration = f.branch(repeated, 120., 500.);
|
||||||
|
f.message(iteration, 160., MessageKind::Create);
|
||||||
|
let optional = f.fragment(iteration, 280., FragmentOperator::Optional);
|
||||||
|
let cleanup = f.branch(optional, 320., 400.);
|
||||||
|
f.message(cleanup, 350., MessageKind::Delete);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, cleanup),
|
||||||
|
vec![(320., 350., Life::Alive), (350., 400., Life::Absent)]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, iteration),
|
||||||
|
vec![
|
||||||
|
(120., 160., Life::Absent),
|
||||||
|
(160., 280., Life::Alive),
|
||||||
|
(400., 500., Life::Unresolved)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
validation::validate(&f.p)
|
||||||
|
.iter()
|
||||||
|
.any(|i| i.code == "lifeline_repetition")
|
||||||
|
);
|
||||||
|
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||||
|
scope: optional,
|
||||||
|
reason: UncertaintyReason::Conditional
|
||||||
|
}));
|
||||||
|
assert!(lane.uncertainties.contains(&Uncertainty {
|
||||||
|
scope: repeated,
|
||||||
|
reason: UncertaintyReason::Repeated
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lifecycle_free_fragments_do_not_poison_other_lanes() {
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
let fragment = f.fragment(f.owner, 100., FragmentOperator::Parallel);
|
||||||
|
let left = f.branch(fragment, 120., 250.);
|
||||||
|
let right = f.branch(fragment, 300., 450.);
|
||||||
|
f.message(left, 200., MessageKind::Delete);
|
||||||
|
let result = f.project();
|
||||||
|
let sender = result.iter().find(|lane| lane.lifeline == f.a).unwrap();
|
||||||
|
assert!(sender.intervals.iter().all(|i| i.life == Life::Alive));
|
||||||
|
assert!(sender.uncertainties.is_empty());
|
||||||
|
let target = result.iter().find(|lane| lane.lifeline == f.b).unwrap();
|
||||||
|
assert!(target.uncertainties.contains(&Uncertainty {
|
||||||
|
scope: fragment,
|
||||||
|
reason: UncertaintyReason::Parallel
|
||||||
|
}));
|
||||||
|
assert_eq!(intervals(target, right), vec![(300., 450., Life::Alive)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explicit_receiver_is_authoritative_even_with_missing_exchange() {
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange { exchange, .. },
|
||||||
|
..
|
||||||
|
}) = f.p.elements.get_mut(&create).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*exchange = None;
|
||||||
|
}
|
||||||
|
assert_eq!(f.lane().markers[0].occurrence, create);
|
||||||
|
if let Some(SharedBody::Occurrence {
|
||||||
|
body: OccurrenceBody::Exchange {
|
||||||
|
receiver, exchange, ..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
}) = f.p.elements.get_mut(&create).unwrap().shared_mut()
|
||||||
|
{
|
||||||
|
*receiver = None;
|
||||||
|
*exchange = Some(f.exchange);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
f.lane().markers.is_empty(),
|
||||||
|
"exchange endpoints never invent a receiver"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_geometry_is_reported_without_nonfinite_or_guessed_markers() {
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
let create = f.message(f.owner, 100., MessageKind::Create);
|
||||||
|
f.rows.insert(create, (f.owner, f32::NAN));
|
||||||
|
let lane = f.lane();
|
||||||
|
assert!(lane.markers.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
lane.uncertainties,
|
||||||
|
vec![Uncertainty {
|
||||||
|
scope: create,
|
||||||
|
reason: UncertaintyReason::MissingGeometry
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
intervals(&lane, f.owner),
|
||||||
|
vec![(0., 600., Life::Unresolved)]
|
||||||
|
);
|
||||||
|
f.scopes.remove(&f.owner);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert!(lane.intervals.is_empty());
|
||||||
|
assert_eq!(lane.uncertainties[0].scope, f.owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_use_is_opaque_and_never_imports_callee_lifecycle_events() {
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
let callee =
|
||||||
|
f.p.add_element(Kind::Scenario, Layer::Oa, "Callee", None)
|
||||||
|
.unwrap();
|
||||||
|
let remote_a =
|
||||||
|
f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote A", Some(callee))
|
||||||
|
.unwrap();
|
||||||
|
let remote_b =
|
||||||
|
f.p.add_element(Kind::Lifeline, Layer::Oa, "Remote B", Some(callee))
|
||||||
|
.unwrap();
|
||||||
|
for (remote, local) in [(remote_a, f.a), (remote_b, f.b)] {
|
||||||
|
let body = f.p.elements[&local].shared().unwrap().clone();
|
||||||
|
*f.p.elements.get_mut(&remote).unwrap().shared_mut().unwrap() = body;
|
||||||
|
}
|
||||||
|
f.occurrence(
|
||||||
|
callee,
|
||||||
|
200.,
|
||||||
|
OccurrenceBody::Exchange {
|
||||||
|
exchange: Some(f.exchange),
|
||||||
|
sender: Some(remote_a),
|
||||||
|
receiver: Some(remote_b),
|
||||||
|
message_kind: MessageKind::Create,
|
||||||
|
operation: None,
|
||||||
|
reply_to: None,
|
||||||
|
values: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
f.occurrence(
|
||||||
|
f.owner,
|
||||||
|
100.,
|
||||||
|
OccurrenceBody::ScenarioUse {
|
||||||
|
scenario: Some(callee),
|
||||||
|
bindings: vec![
|
||||||
|
LifelineBinding {
|
||||||
|
referenced: remote_a,
|
||||||
|
local: f.a,
|
||||||
|
},
|
||||||
|
LifelineBinding {
|
||||||
|
referenced: remote_b,
|
||||||
|
local: f.b,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let lane = f.lane();
|
||||||
|
assert_eq!(intervals(&lane, f.owner), vec![(0., 600., Life::Alive)]);
|
||||||
|
assert!(lane.markers.is_empty());
|
||||||
|
assert!(lane.uncertainties.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn projection_retains_exact_authored_source_and_does_not_enter_history() {
|
||||||
|
use crate::document::Document;
|
||||||
|
let mut f = Fixture::new(Layer::Oa);
|
||||||
|
f.message(f.owner, 100., MessageKind::Create);
|
||||||
|
f.message(f.owner, 300., MessageKind::Delete);
|
||||||
|
let mut source = source_project::format_bundle(&f.p);
|
||||||
|
let text = source.files.get_mut(&source.entry).unwrap();
|
||||||
|
*text = format!("// Retained author note — 雨\r\n{text}\n");
|
||||||
|
let document =
|
||||||
|
Document::from_bundle(source.clone(), source_project::ProjectLimits::default()).unwrap();
|
||||||
|
let accepted = document.project().clone();
|
||||||
|
let geometry = Geometry {
|
||||||
|
rows: &f.rows,
|
||||||
|
scopes: &f.scopes,
|
||||||
|
};
|
||||||
|
let result = project_lifetimes(document.project(), f.owner, &geometry);
|
||||||
|
assert_eq!(result, f.project());
|
||||||
|
assert_eq!(document.bundle(), &source);
|
||||||
|
assert_eq!(document.project(), &accepted);
|
||||||
|
assert!(!document.can_undo());
|
||||||
|
assert!(!document.can_redo());
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ impl Tool {
|
|||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
Source(super::source_draft::Event),
|
||||||
Start(Id, Tool, Option<Id>),
|
Start(Id, Tool, Option<Id>),
|
||||||
Cancel,
|
Cancel,
|
||||||
Submit,
|
Submit,
|
||||||
@@ -90,15 +91,22 @@ pub struct State {
|
|||||||
selected_context: Option<oa_core::topology_context::ContextAddress>,
|
selected_context: Option<oa_core::topology_context::ContextAddress>,
|
||||||
shelf: Option<Shelf>,
|
shelf: Option<Shelf>,
|
||||||
}
|
}
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Browser {
|
struct Browser {
|
||||||
search: String,
|
search: String,
|
||||||
page: usize,
|
page: usize,
|
||||||
}
|
}
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Draft {
|
struct Draft {
|
||||||
|
#[serde(default)]
|
||||||
|
source: super::source_draft::State,
|
||||||
|
#[serde(default = "Id::new_v4")]
|
||||||
|
invocation: Id,
|
||||||
|
#[serde(default)]
|
||||||
|
source_origin: Option<(Id, oa_core::Element)>,
|
||||||
|
|
||||||
#[serde(with = "recovery_insertion")]
|
#[serde(with = "recovery_insertion")]
|
||||||
insertion: Insertion,
|
insertion: Insertion,
|
||||||
fixed: bool,
|
fixed: bool,
|
||||||
@@ -117,6 +125,28 @@ struct Draft {
|
|||||||
contextual: Option<contextual::Draft>,
|
contextual: Option<contextual::Draft>,
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
|
pub(super) fn pending_name(&self) -> Option<&str> {
|
||||||
|
self.draft.as_ref().map(|d| d.name.as_str())
|
||||||
|
}
|
||||||
|
pub(super) fn source_view<'a>(&'a self, p: &'a Project) -> Option<iced::Element<'a, Event>> {
|
||||||
|
self.draft
|
||||||
|
.as_ref()?
|
||||||
|
.source
|
||||||
|
.view(p)
|
||||||
|
.map(|v| v.map(Event::Source))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_active(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.engaged())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_open(&self) -> bool {
|
||||||
|
self.draft.as_ref().is_some_and(|d| d.source.is_open())
|
||||||
|
}
|
||||||
|
pub fn source_back(&mut self) -> bool {
|
||||||
|
self.draft.as_mut().is_some_and(|d| d.source.back())
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
pub(super) fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
||||||
self.draft
|
self.draft
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -152,6 +182,58 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
pub fn update(&mut self, p: &Project, event: Event) -> Result<Option<Command>, String> {
|
||||||
|
if let Event::Source(event) = event {
|
||||||
|
if self
|
||||||
|
.draft
|
||||||
|
.as_mut()
|
||||||
|
.ok_or("Start the consumer form first.")?
|
||||||
|
.source
|
||||||
|
.dismiss(&event)?
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let d = self
|
||||||
|
.draft
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("Start the consumer form first.")?;
|
||||||
|
if d.tool != Tool::Work {
|
||||||
|
return Err("This reference does not support staged source creation.".into());
|
||||||
|
}
|
||||||
|
if d.source_origin.as_ref().is_some_and(|(document, owner)| {
|
||||||
|
*document != p.id || p.elements.get(&d.owner) != Some(owner)
|
||||||
|
}) {
|
||||||
|
return Err("The original consumer changed. Cancel and reopen its form; the current draft is retained.".into());
|
||||||
|
}
|
||||||
|
if d.source_origin.is_none() && d.source.engaged() {
|
||||||
|
return Err("The recovered source draft has no original document binding. Go Back or cancel it.".into());
|
||||||
|
}
|
||||||
|
let mut next = d.clone();
|
||||||
|
if next.source_origin.is_none() && matches!(&event, super::source_draft::Event::Open(_))
|
||||||
|
{
|
||||||
|
next.source_origin = Some((
|
||||||
|
p.id,
|
||||||
|
p.elements
|
||||||
|
.get(&d.owner)
|
||||||
|
.ok_or("The consumer owner no longer exists.")?
|
||||||
|
.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(id) = next.source.update(
|
||||||
|
p,
|
||||||
|
super::source_draft::Scope::Work { owner: d.owner },
|
||||||
|
d.invocation,
|
||||||
|
event,
|
||||||
|
)? {
|
||||||
|
next.target = Some(id);
|
||||||
|
next.command(p)?;
|
||||||
|
}
|
||||||
|
self.draft = Some(next);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if self.source_open() && !matches!(&event, Event::Cancel) {
|
||||||
|
return Err("Use the new source or go Back before editing the parent form.".into());
|
||||||
|
}
|
||||||
|
|
||||||
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
if self.draft.is_some() && matches!(&event, Event::Start(..)) {
|
||||||
return Err(PENDING_DRAFT.into());
|
return Err(PENDING_DRAFT.into());
|
||||||
}
|
}
|
||||||
@@ -184,6 +266,9 @@ impl State {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.draft = Some(Draft {
|
self.draft = Some(Draft {
|
||||||
|
source: Default::default(),
|
||||||
|
invocation: Id::new_v4(),
|
||||||
|
source_origin: Some((p.id, p.elements[&owner].clone())),
|
||||||
insertion: Insertion::default(),
|
insertion: Insertion::default(),
|
||||||
fixed: false,
|
fixed: false,
|
||||||
browsers: Default::default(),
|
browsers: Default::default(),
|
||||||
@@ -508,6 +593,22 @@ fn link(p: &Project, source: Id, target: Id, guard: &str) -> Command {
|
|||||||
}
|
}
|
||||||
impl Draft {
|
impl Draft {
|
||||||
fn command(&self, p: &Project) -> Result<Command, String> {
|
fn command(&self, p: &Project) -> Result<Command, String> {
|
||||||
|
if self.source.engaged()
|
||||||
|
&& self.source_origin.as_ref().is_none_or(|(document, owner)| {
|
||||||
|
*document != p.id || p.elements.get(&self.owner) != Some(owner)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err("The original consumer document or owner changed. Cancel and reopen its form; your draft is retained.".into());
|
||||||
|
}
|
||||||
|
self.source.check_scope(
|
||||||
|
super::source_draft::Scope::Work { owner: self.owner },
|
||||||
|
self.target,
|
||||||
|
)?;
|
||||||
|
let preview = self.source.preview(p, self.target)?;
|
||||||
|
let command = self.consumer_command(preview.as_ref().unwrap_or(p))?;
|
||||||
|
self.source.combine(p, self.target, command)
|
||||||
|
}
|
||||||
|
fn consumer_command(&self, p: &Project) -> Result<Command, String> {
|
||||||
let owner = p
|
let owner = p
|
||||||
.elements
|
.elements
|
||||||
.get(&self.owner)
|
.get(&self.owner)
|
||||||
@@ -615,6 +716,8 @@ impl Draft {
|
|||||||
});
|
});
|
||||||
let name = if self.name.trim().is_empty() {
|
let name = if self.name.trim().is_empty() {
|
||||||
default_name
|
default_name
|
||||||
|
} else if self.source.label(self.target).is_some() {
|
||||||
|
self.name.as_str()
|
||||||
} else {
|
} else {
|
||||||
self.name.trim()
|
self.name.trim()
|
||||||
};
|
};
|
||||||
@@ -1587,6 +1690,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
fn branch(owner: Id, a: Id, b: Id, before: Id, after: Id) -> Draft {
|
fn branch(owner: Id, a: Id, b: Id, before: Id, after: Id) -> Draft {
|
||||||
Draft {
|
Draft {
|
||||||
|
source: Default::default(),
|
||||||
|
invocation: Id::new_v4(),
|
||||||
|
source_origin: None,
|
||||||
insertion: Insertion::default(),
|
insertion: Insertion::default(),
|
||||||
fixed: false,
|
fixed: false,
|
||||||
browsers: Default::default(),
|
browsers: Default::default(),
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub enum Event {
|
|||||||
Search(String),
|
Search(String),
|
||||||
Page(usize),
|
Page(usize),
|
||||||
}
|
}
|
||||||
#[derive(Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub(super) struct Draft {
|
pub(super) struct Draft {
|
||||||
edit: Option<Id>,
|
edit: Option<Id>,
|
||||||
|
|||||||
@@ -206,8 +206,16 @@ impl State {
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pub(crate) fn created_selection(&self, command: &Command) -> Option<Id> {
|
pub(crate) fn created_selection(&self, p: &Project, command: &Command) -> Option<Id> {
|
||||||
let owner = self.pending_owner()?;
|
let owner = self.pending_owner()?;
|
||||||
|
if self
|
||||||
|
.draft
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|d| d.source.label(d.target).is_some())
|
||||||
|
&& self.prepare_pending(p).ok().as_ref() != Some(command)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if let Command::CreateRelation(r) | Command::EditRelation(_, r) = command
|
if let Command::CreateRelation(r) | Command::EditRelation(_, r) = command
|
||||||
&& matches!(&r.body, RelationBody::ContextualLink { owner: caller, .. } if *caller == owner)
|
&& matches!(&r.body, RelationBody::ContextualLink { owner: caller, .. } if *caller == owner)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -369,6 +369,25 @@ pub(super) fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Ele
|
|||||||
.width(Fill)
|
.width(Fill)
|
||||||
.height(Fill)
|
.height(Fill)
|
||||||
.style(style::card);
|
.style(style::card);
|
||||||
|
if !pending && !p.children(owner).any(|e| is_contribution(p, owner, e.id)) {
|
||||||
|
let title = if p.layer_of(owner) == Some(Layer::Oa) {
|
||||||
|
"Add first activity contribution"
|
||||||
|
} else {
|
||||||
|
"Add first function contribution"
|
||||||
|
};
|
||||||
|
header = header.push(button(title).on_press_maybe(
|
||||||
|
(!app.behavior.has_pending() && !app.draft_dirty() && !app.source_dirty()).then(
|
||||||
|
|| {
|
||||||
|
Message::TopologyCanvas(CanvasEvent::Begin {
|
||||||
|
context,
|
||||||
|
tool: Tool::Work,
|
||||||
|
control: ControlKind::Choice,
|
||||||
|
insertion: Insertion::default(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
let details: Element<'_, Message> = match state.shelf {
|
let details: Element<'_, Message> = match state.shelf {
|
||||||
Some(Shelf::Tools) => controls.into(),
|
Some(Shelf::Tools) => controls.into(),
|
||||||
_ => navigation.push(text("Full qualified identities and definition actions appear here for the selected contribution or link.").size(11)).into(),
|
_ => navigation.push(text("Full qualified identities and definition actions appear here for the selected contribution or link.").size(11)).into(),
|
||||||
@@ -383,201 +402,223 @@ pub(super) fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Ele
|
|||||||
};
|
};
|
||||||
let mut form_panel: Element<'_, Message> = iced::widget::Space::new().height(0).into();
|
let mut form_panel: Element<'_, Message> = iced::widget::Space::new().height(0).into();
|
||||||
if let Some(d) = state.draft.as_ref().filter(|d| d.owner == owner) {
|
if let Some(d) = state.draft.as_ref().filter(|d| d.owner == owner) {
|
||||||
let mut form = column![text(d.tool.title()).size(16)].spacing(8);
|
if let Some(child) = d.source.view(p) {
|
||||||
if let Some(contextual) = &d.contextual {
|
form_panel = column![
|
||||||
form = form
|
text(format!(
|
||||||
.push(
|
"Consumer name: {}",
|
||||||
text_input("Link name (retain current when blank)", &d.name)
|
crate::views::short(&d.name, 60)
|
||||||
.on_input(|v| event(Event::Name(v)))
|
))
|
||||||
.padding(8)
|
.size(12),
|
||||||
.size(12),
|
child.map(|e| event(Event::Source(e)))
|
||||||
)
|
]
|
||||||
.push(super::contextual::view(p, owner, contextual));
|
.spacing(6)
|
||||||
|
.height(Fill)
|
||||||
|
.into();
|
||||||
} else {
|
} else {
|
||||||
if let Some(link) = d.insertion.link.and_then(|id| p.relations.get(&id)) {
|
let mut form = column![text(d.tool.title()).size(16)].spacing(8);
|
||||||
let guard = if let RelationBody::SequenceLink { guard, .. } = &link.body {
|
if let Some(contextual) = &d.contextual {
|
||||||
guard.as_str()
|
form = form
|
||||||
} else {
|
.push(
|
||||||
""
|
text_input("Link name (retain current when blank)", &d.name)
|
||||||
};
|
.on_input(|v| event(Event::Name(v)))
|
||||||
form = form.push(label(format!(
|
.padding(8)
|
||||||
"Insert on {} → {} · preserve path identity and condition [{}]",
|
.size(12),
|
||||||
p.named(link.source()),
|
)
|
||||||
p.named(link.target()),
|
.push(super::contextual::view(p, owner, contextual));
|
||||||
guard
|
} else {
|
||||||
)));
|
if let Some(link) = d.insertion.link.and_then(|id| p.relations.get(&id)) {
|
||||||
} else if d.fixed && ordered {
|
let guard = if let RelationBody::SequenceLink { guard, .. } = &link.body {
|
||||||
form = form.push(label(match (d.before, d.after) {
|
guard.as_str()
|
||||||
(Some(before), Some(after)) => format!(
|
} else {
|
||||||
"Insert between {} → {} · preserve the ordered path",
|
""
|
||||||
p.named(before),
|
};
|
||||||
p.named(after)
|
form = form.push(label(format!(
|
||||||
),
|
"Insert on {} → {} · preserve path identity and condition [{}]",
|
||||||
(Some(before), None) => format!("Continue after {}", p.named(before)),
|
p.named(link.source()),
|
||||||
(None, Some(after)) => format!("Insert before {}", p.named(after)),
|
p.named(link.target()),
|
||||||
_ => "Start the ordered path".into(),
|
guard
|
||||||
}));
|
)));
|
||||||
} else if d.fixed
|
} else if d.fixed && ordered {
|
||||||
&& d.tool == Tool::Exchange
|
form = form.push(label(match (d.before, d.after) {
|
||||||
&& let Some(before) = d.before
|
(Some(before), Some(after)) => format!(
|
||||||
{
|
"Insert between {} → {} · preserve the ordered path",
|
||||||
form = form.push(label(format!(
|
p.named(before),
|
||||||
"Append after {} → new exchange · Create adds an explicit sequence link",
|
p.named(after)
|
||||||
p.named(before)
|
),
|
||||||
)));
|
(Some(before), None) => format!("Continue after {}", p.named(before)),
|
||||||
} else if d.fixed && d.tool != Tool::Connect {
|
(None, Some(after)) => format!("Insert before {}", p.named(after)),
|
||||||
form = form.push(label(
|
_ => "Start the ordered path".into(),
|
||||||
d.insertion
|
}));
|
||||||
.before
|
} else if d.fixed
|
||||||
.map_or("Append an unconnected contribution".into(), |id| {
|
&& d.tool == Tool::Exchange
|
||||||
format!("Unconnected contribution before {}", p.named(id))
|
&& let Some(before) = d.before
|
||||||
}),
|
{
|
||||||
));
|
form = form.push(label(format!(
|
||||||
}
|
"Append after {} → new exchange · Create adds an explicit sequence link",
|
||||||
if !legacy_order(p, owner).is_empty() {
|
p.named(before)
|
||||||
form = form.push(label(
|
)));
|
||||||
|
} else if d.fixed && d.tool != Tool::Connect {
|
||||||
|
form = form.push(label(
|
||||||
|
d.insertion
|
||||||
|
.before
|
||||||
|
.map_or("Append an unconnected contribution".into(), |id| {
|
||||||
|
format!("Unconnected contribution before {}", p.named(id))
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if d.tool == Tool::Work {
|
||||||
|
if let Some(title) = d.source.label(d.target) {
|
||||||
|
form = form.push(label(title)).push(label("Create commits the new work and contribution together at the chosen path position."));
|
||||||
|
}
|
||||||
|
form = form.push(button("Create new source…").on_press(event(Event::Source(
|
||||||
|
super::super::source_draft::Event::Open(d.invocation),
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
if !legacy_order(p, owner).is_empty() {
|
||||||
|
form = form.push(label(
|
||||||
"This edit preserves the ordered path as explicit links in the same undo step.",
|
"This edit preserves the ordered path as explicit links in the same undo step.",
|
||||||
));
|
));
|
||||||
}
|
|
||||||
if matches!(
|
|
||||||
d.tool,
|
|
||||||
Tool::Work | Tool::Exchange | Tool::Reuse | Tool::Branch
|
|
||||||
) {
|
|
||||||
let choices = targets(p, owner, d.tool)
|
|
||||||
.into_iter()
|
|
||||||
.filter(|c| compatible_source(p, d, c.id))
|
|
||||||
.collect();
|
|
||||||
if d.tool == Tool::Branch {
|
|
||||||
form = form.push(label("First path work"));
|
|
||||||
}
|
}
|
||||||
form = form.push(picker(
|
if matches!(
|
||||||
choices,
|
d.tool,
|
||||||
d,
|
Tool::Work | Tool::Exchange | Tool::Reuse | Tool::Branch
|
||||||
0,
|
) {
|
||||||
d.target,
|
let choices = targets(p, owner, d.tool)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| compatible_source(p, d, c.id))
|
||||||
|
.collect();
|
||||||
if d.tool == Tool::Branch {
|
if d.tool == Tool::Branch {
|
||||||
"Choose first path work…"
|
form = form.push(label("First path work"));
|
||||||
|
}
|
||||||
|
form = form.push(picker(
|
||||||
|
choices,
|
||||||
|
d,
|
||||||
|
0,
|
||||||
|
d.target,
|
||||||
|
if d.tool == Tool::Branch {
|
||||||
|
"Choose first path work…"
|
||||||
|
} else {
|
||||||
|
"Choose existing source…"
|
||||||
|
},
|
||||||
|
Event::Target,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if d.tool != Tool::Connect {
|
||||||
|
form = form.push(
|
||||||
|
text_input("Contribution name (defaults to source)", &d.name)
|
||||||
|
.on_input(|v| event(Event::Name(v)))
|
||||||
|
.padding(8)
|
||||||
|
.size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if matches!(d.tool, Tool::Control | Tool::Branch) {
|
||||||
|
let kinds = if d.tool == Tool::Branch {
|
||||||
|
vec![ControlKind::Choice, ControlKind::Fork]
|
||||||
} else {
|
} else {
|
||||||
"Choose existing source…"
|
vec![
|
||||||
},
|
ControlKind::Choice,
|
||||||
Event::Target,
|
ControlKind::Merge,
|
||||||
));
|
ControlKind::Fork,
|
||||||
}
|
ControlKind::Join,
|
||||||
if d.tool != Tool::Connect {
|
ControlKind::Loop,
|
||||||
form = form.push(
|
]
|
||||||
text_input("Contribution name (defaults to source)", &d.name)
|
};
|
||||||
.on_input(|v| event(Event::Name(v)))
|
form = form.push(
|
||||||
|
pick_list(
|
||||||
|
kinds.into_iter().map(ControlChoice).collect::<Vec<_>>(),
|
||||||
|
Some(ControlChoice(d.control)),
|
||||||
|
|v| event(Event::Control(v)),
|
||||||
|
)
|
||||||
|
.padding(8)
|
||||||
|
.text_size(12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if d.tool == Tool::Branch {
|
||||||
|
form = form.push(label("Second path work")).push(picker(
|
||||||
|
targets(p, owner, Tool::Work),
|
||||||
|
d,
|
||||||
|
1,
|
||||||
|
d.alternate,
|
||||||
|
"Second path work…",
|
||||||
|
Event::Alternate,
|
||||||
|
));
|
||||||
|
if d.control != ControlKind::Fork {
|
||||||
|
form = form
|
||||||
|
.push(label("First path guard"))
|
||||||
|
.push(
|
||||||
|
text_input("First path guard", &d.guard)
|
||||||
|
.on_input(|v| event(Event::Guard(v)))
|
||||||
|
.padding(8),
|
||||||
|
)
|
||||||
|
.push(label("Second path guard"))
|
||||||
|
.push(
|
||||||
|
text_input("Second path guard (e.g. else)", &d.alternate_guard)
|
||||||
|
.on_input(|v| event(Event::AlternateGuard(v)))
|
||||||
|
.padding(8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.fixed {
|
||||||
|
if let (Some(a), Some(b)) = (d.before, d.after) {
|
||||||
|
form = form.push(label(format!("{} → {}", p.named(a), p.named(b))));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
form = form
|
||||||
|
.push(label("Preceding contribution"))
|
||||||
|
.push(picker(
|
||||||
|
contributions(p, owner),
|
||||||
|
d,
|
||||||
|
2,
|
||||||
|
d.before,
|
||||||
|
"After contribution…",
|
||||||
|
Event::Before,
|
||||||
|
))
|
||||||
|
.push(button("Clear preceding").on_press(event(Event::ClearBefore)))
|
||||||
|
.push(label("Following contribution"))
|
||||||
|
.push(picker(
|
||||||
|
contributions(p, owner),
|
||||||
|
d,
|
||||||
|
3,
|
||||||
|
d.after,
|
||||||
|
"Before contribution…",
|
||||||
|
Event::After,
|
||||||
|
))
|
||||||
|
.push(button("Clear following").on_press(event(Event::ClearAfter)));
|
||||||
|
}
|
||||||
|
if d.tool != Tool::Branch
|
||||||
|
&& d.insertion.link.is_none()
|
||||||
|
&& (d.before.is_some() || d.tool == Tool::Connect)
|
||||||
|
{
|
||||||
|
form = form.push(
|
||||||
|
text_input(
|
||||||
|
"Path condition (empty remains a Choice / Loop warning)",
|
||||||
|
&d.guard,
|
||||||
|
)
|
||||||
|
.on_input(|v| event(Event::Guard(v)))
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.size(12),
|
.size(12),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if matches!(d.tool, Tool::Control | Tool::Branch) {
|
let valid = match d.command(p) {
|
||||||
let kinds = if d.tool == Tool::Branch {
|
Ok(_) => true,
|
||||||
vec![ControlKind::Choice, ControlKind::Fork]
|
Err(problem) => {
|
||||||
|
form = form.push(label(problem));
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let actions = row![
|
||||||
|
button(if d.contextual.is_some() {
|
||||||
|
"Apply link"
|
||||||
} else {
|
} else {
|
||||||
vec![
|
"Create"
|
||||||
ControlKind::Choice,
|
})
|
||||||
ControlKind::Merge,
|
.on_press_maybe(valid.then(|| event(Event::Submit))),
|
||||||
ControlKind::Fork,
|
button("Cancel").on_press(event(Event::Cancel))
|
||||||
ControlKind::Join,
|
]
|
||||||
ControlKind::Loop,
|
.spacing(7);
|
||||||
]
|
form_panel = editor_panel(actions.into(), form.into());
|
||||||
};
|
|
||||||
form = form.push(
|
|
||||||
pick_list(
|
|
||||||
kinds.into_iter().map(ControlChoice).collect::<Vec<_>>(),
|
|
||||||
Some(ControlChoice(d.control)),
|
|
||||||
|v| event(Event::Control(v)),
|
|
||||||
)
|
|
||||||
.padding(8)
|
|
||||||
.text_size(12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if d.tool == Tool::Branch {
|
|
||||||
form = form.push(label("Second path work")).push(picker(
|
|
||||||
targets(p, owner, Tool::Work),
|
|
||||||
d,
|
|
||||||
1,
|
|
||||||
d.alternate,
|
|
||||||
"Second path work…",
|
|
||||||
Event::Alternate,
|
|
||||||
));
|
|
||||||
if d.control != ControlKind::Fork {
|
|
||||||
form = form
|
|
||||||
.push(label("First path guard"))
|
|
||||||
.push(
|
|
||||||
text_input("First path guard", &d.guard)
|
|
||||||
.on_input(|v| event(Event::Guard(v)))
|
|
||||||
.padding(8),
|
|
||||||
)
|
|
||||||
.push(label("Second path guard"))
|
|
||||||
.push(
|
|
||||||
text_input("Second path guard (e.g. else)", &d.alternate_guard)
|
|
||||||
.on_input(|v| event(Event::AlternateGuard(v)))
|
|
||||||
.padding(8),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if d.fixed {
|
|
||||||
if let (Some(a), Some(b)) = (d.before, d.after) {
|
|
||||||
form = form.push(label(format!("{} → {}", p.named(a), p.named(b))));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
form = form
|
|
||||||
.push(label("Preceding contribution"))
|
|
||||||
.push(picker(
|
|
||||||
contributions(p, owner),
|
|
||||||
d,
|
|
||||||
2,
|
|
||||||
d.before,
|
|
||||||
"After contribution…",
|
|
||||||
Event::Before,
|
|
||||||
))
|
|
||||||
.push(button("Clear preceding").on_press(event(Event::ClearBefore)))
|
|
||||||
.push(label("Following contribution"))
|
|
||||||
.push(picker(
|
|
||||||
contributions(p, owner),
|
|
||||||
d,
|
|
||||||
3,
|
|
||||||
d.after,
|
|
||||||
"Before contribution…",
|
|
||||||
Event::After,
|
|
||||||
))
|
|
||||||
.push(button("Clear following").on_press(event(Event::ClearAfter)));
|
|
||||||
}
|
|
||||||
if d.tool != Tool::Branch
|
|
||||||
&& d.insertion.link.is_none()
|
|
||||||
&& (d.before.is_some() || d.tool == Tool::Connect)
|
|
||||||
{
|
|
||||||
form = form.push(
|
|
||||||
text_input(
|
|
||||||
"Path condition (empty remains a Choice / Loop warning)",
|
|
||||||
&d.guard,
|
|
||||||
)
|
|
||||||
.on_input(|v| event(Event::Guard(v)))
|
|
||||||
.padding(8)
|
|
||||||
.size(12),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let valid = match d.command(p) {
|
|
||||||
Ok(_) => true,
|
|
||||||
Err(problem) => {
|
|
||||||
form = form.push(label(problem));
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let actions = row![
|
|
||||||
button(if d.contextual.is_some() {
|
|
||||||
"Apply link"
|
|
||||||
} else {
|
|
||||||
"Create"
|
|
||||||
})
|
|
||||||
.on_press_maybe(valid.then(|| event(Event::Submit))),
|
|
||||||
button("Cancel").on_press(event(Event::Cancel))
|
|
||||||
]
|
|
||||||
.spacing(7);
|
|
||||||
form_panel = editor_panel(actions.into(), form.into());
|
|
||||||
}
|
}
|
||||||
panes(diagram, form_panel, pending)
|
panes(diagram, form_panel, pending)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -445,9 +445,9 @@ fn article(topic: Topic) -> Article {
|
|||||||
walkthrough(
|
walkthrough(
|
||||||
"Describe a path through exchanges",
|
"Describe a path through exchanges",
|
||||||
&[
|
&[
|
||||||
"First create the activities/functions and their exchanges. Check the source and target work for every exchange.",
|
"A blank path offers Add first activity/function contribution. You can use existing work or stage a new source in that form; exchanges still need explicit compatible source and target work.",
|
||||||
"Open Behavior & scenarios → New. Choose Operational process in OA or Functional chain in SA, enter its exact name, then Create and open. The new behavior belongs at the layer root and opens immediately; Undo removes the creation as one edit.",
|
"Open Behavior & scenarios → New. Choose Operational process in OA or Functional chain in SA, enter its exact name, then Create and open. The new behavior belongs at the layer root and opens immediately; Undo removes the creation as one edit.",
|
||||||
"Open Tools and choose Work or Exchange, then a highlighted path or contribution position to involve an existing source. Each contribution keeps its own identity while referring to the shared engineering definition.",
|
"Open Tools and choose Work or Exchange, then a highlighted position. In Work, Create new source keeps the contribution name and position while you name its Activity/Function and choose its semantic parent. Use this new source stages it; Create commits source and contribution in one Undo. An adjacent exchange may require a particular existing work identity.",
|
||||||
"In Tools, choose Sequence link and click or drag from the preceding contribution to the following contribution. Choose Tools → Select / reorder to drag a selected path endpoint and reconnect it; a contribution's ↕ handle changes its position within a column. These are local contributions and sequence links, not new architecture exchanges.",
|
"In Tools, choose Sequence link and click or drag from the preceding contribution to the following contribution. Choose Tools → Select / reorder to drag a selected path endpoint and reconnect it; a contribution's ↕ handle changes its position within a column. These are local contributions and sequence links, not new architecture exchanges.",
|
||||||
"Select a contribution, then add another after it. Tools → Two paths… creates a split, two paths and their merge in one undoable edit. Enter explicit alternative conditions. Fit overview uses the visible canvas area; 100% keeps working scale with scrollbars. If the minimum zoom is reached, scroll to inspect the remainder.",
|
"Select a contribution, then add another after it. Tools → Two paths… creates a split, two paths and their merge in one undoable edit. Enter explicit alternative conditions. Fit overview uses the visible canvas area; 100% keeps working scale with scrollbars. If the minimum zoom is reached, scroll to inspect the remainder.",
|
||||||
"Use Chain involvement from the Process/Functional chain to the capability it supports. Read Model checks for unfinished or disconnected paths.",
|
"Use Chain involvement from the Process/Functional chain to the capability it supports. Read Model checks for unfinished or disconnected paths.",
|
||||||
@@ -484,28 +484,36 @@ fn article(topic: Topic) -> Article {
|
|||||||
const {
|
const {
|
||||||
Article {
|
Article {
|
||||||
summary: "Use a scenario to tell a concrete behavioral story, including repeated messages, actions, state observations and guarded alternatives.",
|
summary: "Use a scenario to tell a concrete behavioral story, including repeated messages, actions, state observations and guarded alternatives.",
|
||||||
keywords: "scenario sequence lifeline sender receiver fragment branch alternative loop guard parallel strict optional occurrence exchange action state interface operation allocation call reply unresolved",
|
keywords: "scenario sequence lifeline lifetime lifecycle birth death capsule sender receiver fragment branch alternative loop guard parallel strict optional occurrence exchange action state interface operation allocation call reply unresolved",
|
||||||
sections: &[
|
sections: &[
|
||||||
walkthrough(
|
walkthrough(
|
||||||
"Create a scenario",
|
"Create a scenario",
|
||||||
&[
|
&[
|
||||||
"Open Behavior & scenarios → New → Scenario and enter its name. Explicitly choose Activities or Participants in OA, or Functions or System boundary in SA. Add optional context only when needed: this is a reference, while the scenario itself belongs at the layer root. Create and open starts the empty scenario.",
|
"Open Behavior & scenarios → New → Scenario and enter its name. Explicitly choose Activities or Participants in OA, or Functions or System boundary in SA. Add optional context only when needed: this is a reference, while the scenario itself belongs at the layer root. Create and open starts the empty scenario.",
|
||||||
"Use + Lifeline and choose the participant or work it represents. Choices follow the scenario's viewpoint: operational activities, operational participants, system functions or system boundary participants.",
|
"Use Add first lifeline on an empty scenario, or + Lifeline. Choose existing work/participant or Create new source. The explicit viewpoint limits its kinds; choose participant kinds deliberately. Source name and lifeline name are separate, and the source semantic parent is independent of the scenario. No allocation or capability involvement is inferred.",
|
||||||
"Choose + Message and its style. Call + correlated reply creates both occurrences in one edit. Select the exchange and compatible sending/receiving lifelines. Reply to existing call offers valid unanswered calls.",
|
"Choose a message style in the tool selector. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. Complete the exchange and details in the form, which retains that sequence position. Escape cancels the gesture.",
|
||||||
"Use Action, Observe state or Fragment for other behavior. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
|
"Call + correlated reply creates both occurrences in one edit. Reply to existing call offers valid unanswered calls. A local timer has an expression and a lifeline, with no global exchange. Create and delete messages describe participant lifetime; Model checks also flag risks inside repeated loops.",
|
||||||
"Choose a message style and Draw message. Start at a highlighted sequence position on a lifeline, then click or drag to the receiving lifeline. The form retains the chosen position while you select the exchange and details. Escape cancels the gesture.",
|
"Choose Add action, Observe state, Reuse scenario or Add fragment and click the intended sequence position. Clicking an action or observation on a lifeline also selects that lifeline. Complete any remaining choices before Create.",
|
||||||
"In Select / reorder, click an occurrence and drag its ↕ handle to another position within the same sequence. The ordered list's up/down controls provide another way to reorder. Select an occurrence to inspect its particular use of the exchange, including its message values.",
|
"In Select / reorder, click an occurrence and drag its ↕ handle to another position within the same sequence. The ordered list's up/down controls provide another way to reorder. Select an occurrence to inspect its particular use of the exchange, including its message values.",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
walkthrough(
|
walkthrough(
|
||||||
"Describe alternatives",
|
"Describe alternatives",
|
||||||
&[
|
&[
|
||||||
"Choose Fragment and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
|
"Choose Add fragment, click its sequence position and select ALT, PAR, LOOP or OPT. Enter the branch conditions; the tool creates the fragment and its operands together.",
|
||||||
"Use the Destination selector in subsequent message or action forms to choose the intended operand. New occurrences append to that operand's ordered behavior.",
|
"Click the highlighted position inside an operand to place the next message, action or reference there. The form shows its destination and insertion position. Append to this sequence instead explicitly moves the pending insertion to that sequence's end.",
|
||||||
"Use Reuse scenario to refer to existing behavior. Bind every referenced lifeline to a compatible local lifeline explicitly. Open source follows the original scenario without copying its contents.",
|
"Choose Reuse scenario and click its position, then bind every referenced lifeline to a compatible local lifeline explicitly. Select the resulting use and choose Open definition. Back to caller returns to that exact use and its viewport after you apply or cancel pending edits.",
|
||||||
"Inspect the scenario projection and Model checks. State exactly what each guard means, including units and assumptions where relevant.",
|
"Inspect the scenario projection and Model checks. State exactly what each guard means, including units and assumptions where relevant.",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
explain(
|
||||||
|
"Read lifeline creation and deletion",
|
||||||
|
"The Lane header and dotted guide identify a lifeline; the guide alone does not establish its lifetime. The stronger solid line shows where the lifeline is known to exist on this scenario path. Create starts that line at a named capsule; Delete ends it at an X. Select the capsule or X to edit that message occurrence. Select the header or guide to inspect the lifeline.",
|
||||||
|
),
|
||||||
|
explain(
|
||||||
|
"Distinguish uncertain lifetime from an unanswered call",
|
||||||
|
"A ? means lifetime depends on an alternative, optional, repeated or parallel path. Operands have independent lifetimes: Delete in one alternative does not delete a lifeline in another. Beyond such a fragment, the guide can continue without a definite solid line. This uncertainty is not by itself a model error. The … beside an activation marks a Call that remains unanswered on that path. A reused scenario's internal Create/Delete events appear when you choose Open definition; they do not create or delete the caller's lifelines.",
|
||||||
|
),
|
||||||
walkthrough(
|
walkthrough(
|
||||||
"Invoke an interface operation",
|
"Invoke an interface operation",
|
||||||
&[
|
&[
|
||||||
@@ -543,7 +551,8 @@ fn article(topic: Topic) -> Article {
|
|||||||
walkthrough(
|
walkthrough(
|
||||||
"Build a state machine",
|
"Build a state machine",
|
||||||
&[
|
&[
|
||||||
"In Behavior & scenarios → New → State machine, enter its name and search for the existing same-layer participant that owns it. Candidate labels show parent context and alias to distinguish repeated names. If the participant does not exist yet, cancel and create it in Architecture first.",
|
"In Behavior & scenarios → New → State machine, enter its name and search for the existing same-layer participant that owns it. Candidate labels show parent context and alias to distinguish repeated names.",
|
||||||
|
"If the owner does not exist, Create new owner retains the machine name and first-view option. Choose its participant kind and semantic parent explicitly. Use this new source returns without changing the model; Create and open commits owner, machine and optional view together.",
|
||||||
"Optionally select Create the first state view, then Create and open. The new machine starts without regions or states. For an existing machine, Create diagram places its existing regions and vertices in a saved view; + View creates another independent layout.",
|
"Optionally select Create the first state view, then Create and open. The new machine starts without regions or states. For an existing machine, Create diagram places its existing regions and vertices in a saved view; + View creates another independent layout.",
|
||||||
"Choose + Region and click the highlighted machine, or a State/Mode for a nested region. Choose Initial, State, Mode or another kind from + State / symbol, then click its highlighted owning region. Rename the new element in Properties.",
|
"Choose + Region and click the highlighted machine, or a State/Mode for a nested region. Choose Initial, State, Mode or another kind from + State / symbol, then click its highlighted owning region. Rename the new element in Properties.",
|
||||||
"Choose Transition and drag from a source vertex to a compatible target, or click the source and then the target. In Select / move, drag states or containers to arrange them; a container moves its descendant placements without changing ownership.",
|
"Choose Transition and drag from a source vertex to a compatible target, or click the source and then the target. In Select / move, drag states or containers to arrange them; a container moves its descendant placements without changing ownership.",
|
||||||
@@ -697,6 +706,10 @@ fn article(topic: Topic) -> Article {
|
|||||||
"Map references by meaning",
|
"Map references by meaning",
|
||||||
"Reference mapping connect exact OA and SA identities used by the proposed change. Similar names are not sufficient. Map the records that play the same engineering role, including enclosing fields in qualified message-value paths. Mapping a referenced record does not authorize creating, moving or editing that record. If it needs a counterpart, establish that correspondence first.",
|
"Reference mapping connect exact OA and SA identities used by the proposed change. Similar names are not sufficient. Map the records that play the same engineering role, including enclosing fields in qualified message-value paths. Mapping a referenced record does not authorize creating, moving or editing that record. If it needs a counterpart, establish that correspondence first.",
|
||||||
),
|
),
|
||||||
|
explain(
|
||||||
|
"Edit stored observations deliberately",
|
||||||
|
"In a trace's Properties, Edit stored history (advanced)… edits the observations retained by that trace. Choose the fact and enter its Operational and System observations explicitly. Not applicable, an observed absent value, and no stored fact have different meanings. Historical references may retain the exact UUID of a deleted record. Apply changes validates the complete history; Undo restores it. These history edits leave both counterpart records unchanged. Use Review counterpart changes… for Keep/Take decisions about their current engineering content.",
|
||||||
|
),
|
||||||
walkthrough(
|
walkthrough(
|
||||||
"Review one message value or lifeline binding",
|
"Review one message value or lifeline binding",
|
||||||
&[
|
&[
|
||||||
@@ -897,7 +910,7 @@ fn article(topic: Topic) -> Article {
|
|||||||
),
|
),
|
||||||
explain(
|
explain(
|
||||||
"A new behavior is still a draft",
|
"A new behavior is still a draft",
|
||||||
"Behavior → New keeps the exact name, owner and viewpoint choices outside the model until Create and open succeeds. Cancel or Escape discards only this draft; if a dialog is open, the first Escape dismisses that dialog. New refuses other unfinished property, source or behavior drafts without applying them. Recovery restores the creation form in its original layer, including rejected raw text, for correction or cancellation.",
|
"Inside a source subform, Back or the first Escape returns to the unchanged parent. Use this new source stages the prerequisite only. Canceling the parent discards it; choosing an existing reference excludes the staged source from creation. Recovery retains both forms and their reserved identities. An open dialog still receives Escape first. Behavior → New keeps the exact name, owner and viewpoint choices outside the model until Create and open succeeds. Cancel or Escape discards only this draft; if a dialog is open, the first Escape dismisses that dialog. New refuses other unfinished property, source or behavior drafts without applying them. Recovery restores the creation form in its original layer, including rejected raw text, for correction or cancellation.",
|
||||||
),
|
),
|
||||||
explain(
|
explain(
|
||||||
"Aliases and references",
|
"Aliases and references",
|
||||||
|
|||||||
@@ -1663,3 +1663,388 @@ fn adding_bend_between_close_ports_stays_outside_owner_clearance() {
|
|||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn child_work_preserves_allocated_context_and_atomic_history() {
|
||||||
|
use oa_core::projection::project_view;
|
||||||
|
for (layer, performer_kind, work_kind) in [
|
||||||
|
(Layer::Oa, Kind::Entity, Kind::Activity),
|
||||||
|
(Layer::Sa, Kind::System, Kind::Function),
|
||||||
|
] {
|
||||||
|
for folded in [false, true] {
|
||||||
|
let mut p = Project::empty();
|
||||||
|
let performer = p
|
||||||
|
.add_element(performer_kind, layer, "Performer", None)
|
||||||
|
.unwrap();
|
||||||
|
let work = p
|
||||||
|
.add_element(work_kind, layer, "Parent work", None)
|
||||||
|
.unwrap();
|
||||||
|
let allocation = p.relate(
|
||||||
|
RelationKind::Allocation,
|
||||||
|
"Engineering assignment",
|
||||||
|
performer,
|
||||||
|
work,
|
||||||
|
);
|
||||||
|
p.relations.get_mut(&allocation).unwrap().description =
|
||||||
|
"Retain assignment metadata".into();
|
||||||
|
let original = p.relations[&allocation].clone();
|
||||||
|
let diagram = p.ensure_diagram(layer);
|
||||||
|
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||||
|
let performer_node = p.place(diagram, performer, Position { x: 60., y: 60. });
|
||||||
|
let work_node = p.place(diagram, work, Position { x: 84., y: 124. });
|
||||||
|
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||||
|
for node in [performer_node, work_node] {
|
||||||
|
p.diagrams
|
||||||
|
.get_mut(&diagram)
|
||||||
|
.unwrap()
|
||||||
|
.nodes
|
||||||
|
.get_mut(&node)
|
||||||
|
.unwrap()
|
||||||
|
.collapsed = folded;
|
||||||
|
}
|
||||||
|
let mut app = App::with_project(p);
|
||||||
|
send(&mut app, Message::Layer(layer));
|
||||||
|
send(&mut app, Message::Page(Page::Diagram));
|
||||||
|
send(
|
||||||
|
&mut app,
|
||||||
|
Message::Diagram(crate::app::Choice {
|
||||||
|
id: diagram,
|
||||||
|
label: String::new(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let mut first = None;
|
||||||
|
for step in 0..3 {
|
||||||
|
let parent = if step == 2 { first.unwrap() } else { work };
|
||||||
|
send(&mut app, Message::Select(parent));
|
||||||
|
let before = app.project().clone();
|
||||||
|
let before_source = app.document.bundle().clone();
|
||||||
|
send(&mut app, Message::CreateContextElement(work_kind, true));
|
||||||
|
let created = app.selected;
|
||||||
|
assert!(
|
||||||
|
!before.elements.contains_key(&created),
|
||||||
|
"{layer:?} step {step}: {}",
|
||||||
|
app.status
|
||||||
|
);
|
||||||
|
assert_eq!(app.project().elements.len(), before.elements.len() + 1);
|
||||||
|
assert_eq!(app.project().elements[&created].parent, Some(parent));
|
||||||
|
for (id, element) in &before.elements {
|
||||||
|
assert_eq!(&app.project().elements[id], element);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
!oa_core::validation::validate(app.project())
|
||||||
|
.iter()
|
||||||
|
.any(|i| i.severity == oa_core::validation::Severity::Error)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::allocation_summary(app.project(), work),
|
||||||
|
Some(performer)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::effective_performer(app.project(), created),
|
||||||
|
Some(performer)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!app.project()
|
||||||
|
.relations
|
||||||
|
.values()
|
||||||
|
.any(|r| r.kind() == RelationKind::Allocation && r.target() == parent)
|
||||||
|
);
|
||||||
|
if step != 1 {
|
||||||
|
let mut expected = original.clone();
|
||||||
|
if let RelationBody::Allocation { target, .. } = &mut expected.body {
|
||||||
|
*target = created;
|
||||||
|
}
|
||||||
|
assert_eq!(app.project().relations[&allocation], expected);
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
app.project().relations[&allocation],
|
||||||
|
before.relations[&allocation]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
app.project().relations.len(),
|
||||||
|
before.relations.len() + usize::from(step == 1)
|
||||||
|
);
|
||||||
|
let view = project_view(app.project(), diagram);
|
||||||
|
let parent_node = view.nodes.iter().find(|n| n.element == parent).unwrap();
|
||||||
|
let child_node = view.nodes.iter().find(|n| n.element == created).unwrap();
|
||||||
|
let root_node = view.nodes.iter().find(|n| n.element == work).unwrap();
|
||||||
|
assert_eq!(root_node.visual_parent, Some(performer_node));
|
||||||
|
assert_eq!(child_node.visual_parent, Some(parent_node.id));
|
||||||
|
assert!(!app.project().diagrams[&diagram].nodes[&performer_node].collapsed);
|
||||||
|
assert!(!app.project().diagrams[&diagram].nodes[&parent_node.id].collapsed);
|
||||||
|
assert!(child_node.position.x >= parent_node.position.x);
|
||||||
|
assert!(child_node.position.y >= parent_node.position.y + 64.);
|
||||||
|
assert!(
|
||||||
|
child_node.position.x + child_node.size.width
|
||||||
|
<= parent_node.position.x + parent_node.size.width
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
child_node.position.y + child_node.size.height
|
||||||
|
<= parent_node.position.y + parent_node.size.height
|
||||||
|
);
|
||||||
|
for sibling in view
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.filter(|n| n.visual_parent == Some(parent_node.id) && n.id != child_node.id)
|
||||||
|
{
|
||||||
|
assert!(sibling.position.y + sibling.size.height <= child_node.position.y);
|
||||||
|
}
|
||||||
|
let after = app.project().clone();
|
||||||
|
let after_source = app.document.bundle().clone();
|
||||||
|
send(&mut app, Message::Undo);
|
||||||
|
assert_eq!(
|
||||||
|
app.project(),
|
||||||
|
&before,
|
||||||
|
"creation, allocation and reveal/reflow undo together"
|
||||||
|
);
|
||||||
|
assert_eq!(app.document.bundle(), &before_source);
|
||||||
|
send(&mut app, Message::Redo);
|
||||||
|
assert_eq!(app.project(), &after);
|
||||||
|
assert_eq!(app.document.bundle(), &after_source);
|
||||||
|
if step == 0 {
|
||||||
|
first = Some(created);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn child_work_does_not_invent_unallocated_or_mixed_performers() {
|
||||||
|
for (layer, performer_kind, other_kind, work_kind) in [
|
||||||
|
(Layer::Oa, Kind::Entity, Kind::Entity, Kind::Activity),
|
||||||
|
(Layer::Sa, Kind::System, Kind::SystemActor, Kind::Function),
|
||||||
|
] {
|
||||||
|
// Root leaf, partly unallocated composite, and mixed-performer composite.
|
||||||
|
for context in 0..3 {
|
||||||
|
let mut p = Project::empty();
|
||||||
|
let performer = p
|
||||||
|
.add_element(performer_kind, layer, "Performer", None)
|
||||||
|
.unwrap();
|
||||||
|
let other = p
|
||||||
|
.add_element(other_kind, layer, "Other performer", None)
|
||||||
|
.unwrap();
|
||||||
|
let work = p
|
||||||
|
.add_element(work_kind, layer, "Parent work", None)
|
||||||
|
.unwrap();
|
||||||
|
let mut placed = vec![performer, other, work];
|
||||||
|
if context != 0 {
|
||||||
|
let first = p
|
||||||
|
.add_element(work_kind, layer, "First leaf", Some(work))
|
||||||
|
.unwrap();
|
||||||
|
let second = p
|
||||||
|
.add_element(work_kind, layer, "Second leaf", Some(work))
|
||||||
|
.unwrap();
|
||||||
|
p.relate(
|
||||||
|
RelationKind::Allocation,
|
||||||
|
"First assignment",
|
||||||
|
performer,
|
||||||
|
first,
|
||||||
|
);
|
||||||
|
if context == 2 {
|
||||||
|
p.relate(RelationKind::Allocation, "Other assignment", other, second);
|
||||||
|
}
|
||||||
|
placed.extend([first, second]);
|
||||||
|
}
|
||||||
|
assert_eq!(oa_core::validation::allocation_summary(&p, work), None);
|
||||||
|
let diagram = p.ensure_diagram(layer);
|
||||||
|
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||||
|
for id in placed {
|
||||||
|
p.place(diagram, id, Position { x: 60., y: 60. });
|
||||||
|
}
|
||||||
|
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||||
|
let mut app = App::with_project(p);
|
||||||
|
send(&mut app, Message::Layer(layer));
|
||||||
|
send(&mut app, Message::Page(Page::Diagram));
|
||||||
|
send(
|
||||||
|
&mut app,
|
||||||
|
Message::Diagram(crate::app::Choice {
|
||||||
|
id: diagram,
|
||||||
|
label: String::new(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
send(&mut app, Message::Select(work));
|
||||||
|
let before = app.project().clone();
|
||||||
|
let before_source = app.document.bundle().clone();
|
||||||
|
send(&mut app, Message::CreateContextElement(work_kind, true));
|
||||||
|
let child = app.selected;
|
||||||
|
assert!(!before.elements.contains_key(&child), "{}", app.status);
|
||||||
|
assert_eq!(app.project().elements[&child].parent, Some(work));
|
||||||
|
assert_eq!(app.project().relations, before.relations);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::effective_performer(app.project(), child),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::allocation_summary(app.project(), work),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
let after = app.project().clone();
|
||||||
|
let after_source = app.document.bundle().clone();
|
||||||
|
send(&mut app, Message::Undo);
|
||||||
|
assert_eq!(app.project(), &before);
|
||||||
|
assert_eq!(app.document.bundle(), &before_source);
|
||||||
|
send(&mut app, Message::Redo);
|
||||||
|
assert_eq!(app.project(), &after);
|
||||||
|
assert_eq!(app.document.bundle(), &after_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn child_work_transfers_leaf_roles_without_fabricating_responsibilities() {
|
||||||
|
// Direct allocation + matching roles, uniquely realized roles only,
|
||||||
|
// unresolved roles, and roles realized by different performers.
|
||||||
|
for context in 0..4 {
|
||||||
|
let mut p = Project::empty();
|
||||||
|
let performer = p
|
||||||
|
.add_element(Kind::Entity, Layer::Oa, "Performer", None)
|
||||||
|
.unwrap();
|
||||||
|
let other = p
|
||||||
|
.add_element(Kind::Entity, Layer::Oa, "Other performer", None)
|
||||||
|
.unwrap();
|
||||||
|
let work = p
|
||||||
|
.add_element(Kind::Activity, Layer::Oa, "Responsible work", None)
|
||||||
|
.unwrap();
|
||||||
|
let mut responsibilities = Vec::new();
|
||||||
|
if context == 0 {
|
||||||
|
responsibilities.push(p.relate(
|
||||||
|
RelationKind::Allocation,
|
||||||
|
"Direct responsibility",
|
||||||
|
performer,
|
||||||
|
work,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for index in 0..2 {
|
||||||
|
let role = p
|
||||||
|
.add_element(Kind::Role, Layer::Oa, &format!("Role {index}"), None)
|
||||||
|
.unwrap();
|
||||||
|
responsibilities.push(p.relate(
|
||||||
|
RelationKind::RoleAssignment,
|
||||||
|
&format!("Assigned role {index}"),
|
||||||
|
role,
|
||||||
|
work,
|
||||||
|
));
|
||||||
|
if context != 2 {
|
||||||
|
let source = if context == 3 && index == 1 {
|
||||||
|
other
|
||||||
|
} else {
|
||||||
|
performer
|
||||||
|
};
|
||||||
|
p.relate(RelationKind::RoleRealization, "Realizes role", source, role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, id) in responsibilities.iter().enumerate() {
|
||||||
|
let relation = p.relations.get_mut(id).unwrap();
|
||||||
|
relation.description =
|
||||||
|
format!("Responsibility {index}: preserve engineering rationale");
|
||||||
|
let link = Id::new_v4();
|
||||||
|
p.artifact_links.insert(
|
||||||
|
link,
|
||||||
|
ArtifactLink {
|
||||||
|
id: link,
|
||||||
|
alias: format!("responsibility_evidence_{index}"),
|
||||||
|
name: format!("Responsibility evidence {index}"),
|
||||||
|
description: "Preserve the reference to this exact relation".into(),
|
||||||
|
owner: *id,
|
||||||
|
relation: ArtifactRelation::References,
|
||||||
|
uri: format!("https://example.invalid/responsibility/{index}"),
|
||||||
|
revision: Some("review-7".into()),
|
||||||
|
fragment: Some("assignment".into()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let original_relations = p.relations.clone();
|
||||||
|
let diagram = p.ensure_diagram(Layer::Oa);
|
||||||
|
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||||
|
p.place(diagram, performer, Position { x: 60., y: 60. });
|
||||||
|
p.place(diagram, work, Position { x: 84., y: 124. });
|
||||||
|
p = execute_batch(&p, &[Command::Layout(diagram)]).unwrap();
|
||||||
|
let expected_performer = (context < 2).then_some(performer);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::allocation_summary(&p, work),
|
||||||
|
expected_performer
|
||||||
|
);
|
||||||
|
let mut app = App::with_project(p);
|
||||||
|
send(&mut app, Message::Page(Page::Diagram));
|
||||||
|
send(
|
||||||
|
&mut app,
|
||||||
|
Message::Diagram(crate::app::Choice {
|
||||||
|
id: diagram,
|
||||||
|
label: String::new(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let mut first = None;
|
||||||
|
for step in 0..2 {
|
||||||
|
send(&mut app, Message::Select(work));
|
||||||
|
let before = app.project().clone();
|
||||||
|
let before_source = app.document.bundle().clone();
|
||||||
|
send(
|
||||||
|
&mut app,
|
||||||
|
Message::CreateContextElement(Kind::Activity, true),
|
||||||
|
);
|
||||||
|
let child = app.selected;
|
||||||
|
assert!(
|
||||||
|
!before.elements.contains_key(&child),
|
||||||
|
"context {context}, step {step}: {}",
|
||||||
|
app.status
|
||||||
|
);
|
||||||
|
assert_eq!(app.project().elements[&child].parent, Some(work));
|
||||||
|
if step == 0 {
|
||||||
|
first = Some(child);
|
||||||
|
}
|
||||||
|
for (id, original) in &original_relations {
|
||||||
|
let mut expected = original.clone();
|
||||||
|
if responsibilities.contains(id) {
|
||||||
|
match &mut expected.body {
|
||||||
|
RelationBody::Allocation { target, .. }
|
||||||
|
| RelationBody::RoleAssignment { target, .. } => *target = first.unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(app.project().relations[id], expected);
|
||||||
|
}
|
||||||
|
assert_eq!(app.project().artifact_links, before.artifact_links);
|
||||||
|
assert!(!app.project().artifact_links.is_empty());
|
||||||
|
let added = usize::from(step == 1 && expected_performer.is_some());
|
||||||
|
assert_eq!(
|
||||||
|
app.project().relations.len(),
|
||||||
|
before.relations.len() + added
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
oa_core::validation::effective_performer(app.project(), child),
|
||||||
|
expected_performer
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.project()
|
||||||
|
.relations
|
||||||
|
.values()
|
||||||
|
.filter(|r| r.kind() == RelationKind::RoleAssignment && r.target() == child)
|
||||||
|
.count(),
|
||||||
|
if step == 0 { 2 } else { 0 }
|
||||||
|
);
|
||||||
|
if context != 0 && step == 0 {
|
||||||
|
assert!(
|
||||||
|
!app.project()
|
||||||
|
.relations
|
||||||
|
.values()
|
||||||
|
.any(|r| r.kind() == RelationKind::Allocation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
!oa_core::validation::validate(app.project())
|
||||||
|
.iter()
|
||||||
|
.any(|i| i.severity == oa_core::validation::Severity::Error)
|
||||||
|
);
|
||||||
|
let after = app.project().clone();
|
||||||
|
let after_source = app.document.bundle().clone();
|
||||||
|
send(&mut app, Message::Undo);
|
||||||
|
assert_eq!(app.project(), &before);
|
||||||
|
assert_eq!(app.document.bundle(), &before_source);
|
||||||
|
send(&mut app, Message::Redo);
|
||||||
|
assert_eq!(app.project(), &after);
|
||||||
|
assert_eq!(app.document.bundle(), &after_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
Run from any directory with Python 3: semantic_schema.py [--check] [--validate].
|
Run from any directory with Python 3: semantic_schema.py [--check] [--validate].
|
||||||
--validate additionally runs source-7 structural fixtures with jsonschema.
|
--validate additionally runs source-7 structural fixtures with jsonschema.
|
||||||
This generates documentation only, never runtime selectors or model mutations.
|
The desktop stored-history editor reads the checked-in descriptor for field
|
||||||
|
shapes. Core types and validation remain authoritative for model mutations.
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
|||||||
@@ -1319,16 +1319,25 @@ references a Call occurrence in the same scenario. The call MUST precede the
|
|||||||
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
||||||
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
||||||
or use an explicit reverse-direction exchange. A second reply to the same call
|
or use an explicit reverse-direction exchange. A second reply to the same call
|
||||||
on that guaranteed path is an error. Missing reply correlation is a draft
|
that can follow another reply on a shared execution path, including conditional
|
||||||
|
or parallel operands, is an error. Missing reply correlation is a draft
|
||||||
warning; non-Reply messages MUST NOT have <span class="docutils literal">reply_to</span>.</p>
|
warning; non-Reply messages MUST NOT have <span class="docutils literal">reply_to</span>.</p>
|
||||||
<p>Create identifies the receiver lifeline's creation. An earlier guaranteed use
|
<p>Create identifies the receiver lifeline's creation. Another use that can precede
|
||||||
of that lifeline is an error. Delete identifies its destruction; a later
|
its creation on a shared execution path is an error. Delete identifies its
|
||||||
guaranteed use is an error. Ordering checks respect branch ownership; they MUST
|
destruction; another use that can follow it on a shared execution path is an
|
||||||
NOT flatten alternative branches into a false unconditional order. A Timer is
|
error. These checks include conditional and parallel operands but exclude
|
||||||
|
mutually exclusive alternatives. Ordering checks respect branch ownership; they
|
||||||
|
MUST NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||||
local to its sender lifeline, has no global exchange or values, and has either
|
local to its sender lifeline, has no global exchange or values, and has either
|
||||||
no receiver or the same receiver as sender. Its nonempty expression describes
|
no receiver or the same receiver as sender. Its nonempty expression describes
|
||||||
the delay/deadline; an empty expression warns. Conditions and expressions are
|
the delay/deadline; an empty expression warns. Conditions and expressions are
|
||||||
stored engineering annotations, not an executable simulation language.</p>
|
stored engineering annotations, not an executable simulation language.</p>
|
||||||
|
<p>These ordering checks compare distinct stored occurrences along structural
|
||||||
|
paths; they do not simulate repeated loop iterations. Repetition risks use
|
||||||
|
<span class="docutils literal">reply_repetition</span> and <span class="docutils literal">lifeline_repetition</span> warnings separately. For example,
|
||||||
|
a loop containing one Reply to an outer Call can warn about repetition without
|
||||||
|
containing two distinct replies; a same-operand Create/use/Delete lifetime can
|
||||||
|
repeat without its previous iteration being treated as an earlier stored use.</p>
|
||||||
<p>Each MessageValue is <span class="docutils literal">{ path [@parameter]; field @data_field; expression "value expression"; }</span>.
|
<p>Each MessageValue is <span class="docutils literal">{ path [@parameter]; field @data_field; expression "value expression"; }</span>.
|
||||||
Its identity is the pair (path, field), so two uses of one Record can have
|
Its identity is the pair (path, field), so two uses of one Record can have
|
||||||
different values for the same terminal field. Path contains enclosing DataField
|
different values for the same terminal field. Path contains enclosing DataField
|
||||||
@@ -2640,6 +2649,41 @@ leaving dangling references. Deleting a sequence does not delete the semantic
|
|||||||
exchanges it references. Cross-layer counterparts are not owned descendants.
|
exchanges it references. Cross-layer counterparts are not owned descendants.
|
||||||
Text edits describe a complete candidate and must remove or repair dependencies
|
Text edits describe a complete candidate and must remove or repair dependencies
|
||||||
explicitly before Apply succeeds.</p>
|
explicitly before Apply succeeds.</p>
|
||||||
|
<section id="scenario-lifetime-presentation">
|
||||||
|
<h3>8.1. Scenario lifetime presentation</h3>
|
||||||
|
<p>Scenario lifetime geometry is derived from the stored occurrences and their
|
||||||
|
sequence/operand ownership. It adds no model records, source annotations or
|
||||||
|
history entries. A lifeline's identity header and neutral editing guide remain
|
||||||
|
available before creation, after deletion and when its represented element is
|
||||||
|
unfinished. The guide MUST be visually distinguishable from a live interval;
|
||||||
|
it is an editing axis, not a statement that the participant exists.</p>
|
||||||
|
<p>A directly ordered Create starts the receiver's live interval at its occurrence;
|
||||||
|
Delete ends that receiver's interval at its occurrence. Create uses a named birth
|
||||||
|
marker and Delete an X, each selectable through the original occurrence identity.
|
||||||
|
The message's receiving endpoint MUST align with its marker, including self
|
||||||
|
messages. A pre-existing lifeline without a creation has a live interval from
|
||||||
|
below its header; without deletion it extends to the visible scenario bottom,
|
||||||
|
including in an empty scenario. A missing lifecycle receiver MUST NOT be inferred from
|
||||||
|
the exchange. An explicit local receiver remains meaningful when its exchange
|
||||||
|
reference is unfinished.</p>
|
||||||
|
<p>Operands MUST be projected independently from their enclosing entry context.
|
||||||
|
A lifetime change in an alternative, optional, loop or parallel operand MUST
|
||||||
|
NOT imply the same change in its displayed sibling or a definite lifetime
|
||||||
|
after the fragment. When local structure does not establish one lifetime across
|
||||||
|
the represented paths, the continuation is visually unresolved. A subsequent
|
||||||
|
explicit Create or Delete in the enclosing sequence can establish a definite interval.
|
||||||
|
Neither guard text, screen order nor a guessed loop count proves execution.
|
||||||
|
ScenarioUse remains opaque: opening its definition may show its lifecycle,
|
||||||
|
but a callee's geometry MUST NOT create or destroy a caller lifeline.</p>
|
||||||
|
<p>Call activation is distinct from lifetime. A branch-local Delete clips that
|
||||||
|
branch's visible activation at the death marker without inventing a Reply,
|
||||||
|
removing an unresolved-call warning or terminating sibling paths.
|
||||||
|
Solid activation bars are restricted to definite live intervals; an unresolved
|
||||||
|
continuation retains its uncertainty/call diagnostic rather than depicting
|
||||||
|
definite survival after a possible deletion. Selection,
|
||||||
|
insertion slots, source edits and Undo/Redo retain the same semantic identities
|
||||||
|
and validation boundary regardless of the rendered intervals.</p>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section id="persistence-and-explicit-migration">
|
<section id="persistence-and-explicit-migration">
|
||||||
<h2>9. Persistence and explicit migration</h2>
|
<h2>9. Persistence and explicit migration</h2>
|
||||||
|
|||||||
@@ -680,18 +680,28 @@ references a Call occurrence in the same scenario. The call MUST precede the
|
|||||||
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
reply on a guaranteed common sequence/enclosing branch path. Reply lifelines
|
||||||
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
reverse the call's sender/receiver; it may reuse the call's exchange in reverse
|
||||||
or use an explicit reverse-direction exchange. A second reply to the same call
|
or use an explicit reverse-direction exchange. A second reply to the same call
|
||||||
on that guaranteed path is an error. Missing reply correlation is a draft
|
that can follow another reply on a shared execution path, including conditional
|
||||||
|
or parallel operands, is an error. Missing reply correlation is a draft
|
||||||
warning; non-Reply messages MUST NOT have ``reply_to``.
|
warning; non-Reply messages MUST NOT have ``reply_to``.
|
||||||
|
|
||||||
Create identifies the receiver lifeline's creation. An earlier guaranteed use
|
Create identifies the receiver lifeline's creation. Another use that can precede
|
||||||
of that lifeline is an error. Delete identifies its destruction; a later
|
its creation on a shared execution path is an error. Delete identifies its
|
||||||
guaranteed use is an error. Ordering checks respect branch ownership; they MUST
|
destruction; another use that can follow it on a shared execution path is an
|
||||||
NOT flatten alternative branches into a false unconditional order. A Timer is
|
error. These checks include conditional and parallel operands but exclude
|
||||||
|
mutually exclusive alternatives. Ordering checks respect branch ownership; they
|
||||||
|
MUST NOT flatten alternative branches into a false unconditional order. A Timer is
|
||||||
local to its sender lifeline, has no global exchange or values, and has either
|
local to its sender lifeline, has no global exchange or values, and has either
|
||||||
no receiver or the same receiver as sender. Its nonempty expression describes
|
no receiver or the same receiver as sender. Its nonempty expression describes
|
||||||
the delay/deadline; an empty expression warns. Conditions and expressions are
|
the delay/deadline; an empty expression warns. Conditions and expressions are
|
||||||
stored engineering annotations, not an executable simulation language.
|
stored engineering annotations, not an executable simulation language.
|
||||||
|
|
||||||
|
These ordering checks compare distinct stored occurrences along structural
|
||||||
|
paths; they do not simulate repeated loop iterations. Repetition risks use
|
||||||
|
``reply_repetition`` and ``lifeline_repetition`` warnings separately. For example,
|
||||||
|
a loop containing one Reply to an outer Call can warn about repetition without
|
||||||
|
containing two distinct replies; a same-operand Create/use/Delete lifetime can
|
||||||
|
repeat without its previous iteration being treated as an earlier stored use.
|
||||||
|
|
||||||
Each MessageValue is ``{ path [@parameter]; field @data_field; expression "value expression"; }``.
|
Each MessageValue is ``{ path [@parameter]; field @data_field; expression "value expression"; }``.
|
||||||
Its identity is the pair (path, field), so two uses of one Record can have
|
Its identity is the pair (path, field), so two uses of one Record can have
|
||||||
different values for the same terminal field. Path contains enclosing DataField
|
different values for the same terminal field. Path contains enclosing DataField
|
||||||
@@ -2062,6 +2072,45 @@ exchanges it references. Cross-layer counterparts are not owned descendants.
|
|||||||
Text edits describe a complete candidate and must remove or repair dependencies
|
Text edits describe a complete candidate and must remove or repair dependencies
|
||||||
explicitly before Apply succeeds.
|
explicitly before Apply succeeds.
|
||||||
|
|
||||||
|
8.1. Scenario lifetime presentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Scenario lifetime geometry is derived from the stored occurrences and their
|
||||||
|
sequence/operand ownership. It adds no model records, source annotations or
|
||||||
|
history entries. A lifeline's identity header and neutral editing guide remain
|
||||||
|
available before creation, after deletion and when its represented element is
|
||||||
|
unfinished. The guide MUST be visually distinguishable from a live interval;
|
||||||
|
it is an editing axis, not a statement that the participant exists.
|
||||||
|
|
||||||
|
A directly ordered Create starts the receiver's live interval at its occurrence;
|
||||||
|
Delete ends that receiver's interval at its occurrence. Create uses a named birth
|
||||||
|
marker and Delete an X, each selectable through the original occurrence identity.
|
||||||
|
The message's receiving endpoint MUST align with its marker, including self
|
||||||
|
messages. A pre-existing lifeline without a creation has a live interval from
|
||||||
|
below its header; without deletion it extends to the visible scenario bottom,
|
||||||
|
including in an empty scenario. A missing lifecycle receiver MUST NOT be inferred from
|
||||||
|
the exchange. An explicit local receiver remains meaningful when its exchange
|
||||||
|
reference is unfinished.
|
||||||
|
|
||||||
|
Operands MUST be projected independently from their enclosing entry context.
|
||||||
|
A lifetime change in an alternative, optional, loop or parallel operand MUST
|
||||||
|
NOT imply the same change in its displayed sibling or a definite lifetime
|
||||||
|
after the fragment. When local structure does not establish one lifetime across
|
||||||
|
the represented paths, the continuation is visually unresolved. A subsequent
|
||||||
|
explicit Create or Delete in the enclosing sequence can establish a definite interval.
|
||||||
|
Neither guard text, screen order nor a guessed loop count proves execution.
|
||||||
|
ScenarioUse remains opaque: opening its definition may show its lifecycle,
|
||||||
|
but a callee's geometry MUST NOT create or destroy a caller lifeline.
|
||||||
|
|
||||||
|
Call activation is distinct from lifetime. A branch-local Delete clips that
|
||||||
|
branch's visible activation at the death marker without inventing a Reply,
|
||||||
|
removing an unresolved-call warning or terminating sibling paths.
|
||||||
|
Solid activation bars are restricted to definite live intervals; an unresolved
|
||||||
|
continuation retains its uncertainty/call diagnostic rather than depicting
|
||||||
|
definite survival after a possible deletion. Selection,
|
||||||
|
insertion slots, source edits and Undo/Redo retain the same semantic identities
|
||||||
|
and validation boundary regardless of the rendered intervals.
|
||||||
|
|
||||||
9. Persistence and explicit migration
|
9. Persistence and explicit migration
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Linux packaging and deployment inputs
|
# Linux packaging and deployment inputs
|
||||||
|
|
||||||
The packaging command stages **already-built** desktop/server executables for `x86_64-unknown-linux-gnu`. It runs on Windows PowerShell 5.1 or PowerShell 7 (including Linux) with Python 3.11+ standard library, Git, and the matching Rust/Cargo dependency cache. It does not build, download, install runtime libraries or launch the application. Python creates and reads back the tar archive; end users do not need Python, PowerShell or Rust.
|
The packaging command stages **already-built** desktop, server and source CLI executables for `x86_64-unknown-linux-gnu`. It runs on Windows PowerShell 5.1 or PowerShell 7 (including Linux) with Python 3.11+ standard library, Git, and the matching Rust/Cargo dependency cache. It does not build, download, install runtime libraries or launch the application. Python creates and reads back the tar archive; end users do not need Python, PowerShell or Rust.
|
||||||
|
|
||||||
Build and validate first in a source checkout. The current runtime validation baseline is Ubuntu 24.04 x86-64. A glibc symbol inventory is not a claim that every distribution with that glibc version supports the renderer and dialogs.
|
Build and validate first in a source checkout. The current runtime validation baseline is Ubuntu 24.04 x86-64. A glibc symbol inventory is not a claim that every distribution with that glibc version supports the renderer and dialogs.
|
||||||
|
|
||||||
@@ -9,23 +9,25 @@ Build and validate first in a source checkout. The current runtime validation ba
|
|||||||
-Version 0.2.0-rc1 -BinaryDirectory ./target/release -Python python3
|
-Version 0.2.0-rc1 -BinaryDirectory ./target/release -Python python3
|
||||||
```
|
```
|
||||||
|
|
||||||
For an explicit-target build, use `target/x86_64-unknown-linux-gnu/release`. A cross-build may supply another directory containing both `arcadia-studio` and `arcadia-server`. Select the Rust compiler used for the intended build before packaging; its Linux target libraries, copyright reports and locked source/archive cache must be installed. Read-only `cargo metadata --locked --offline --filter-platform x86_64-unknown-linux-gnu` and Rust version/sysroot queries are the only Rust commands run by packaging. When packaging from Windows, `-Python C:/path/to/python.exe` selects Python explicitly.
|
For an explicit-target build, use `target/x86_64-unknown-linux-gnu/release`. A cross-build may supply another directory containing all three executables: `arcadia-studio`, `arcadia-server` and `arcadia`. Select the Rust compiler used for the intended build before packaging; its Linux target libraries, copyright reports and locked source/archive cache must be installed. Read-only `cargo metadata --locked --offline --filter-platform x86_64-unknown-linux-gnu` and Rust version/sysroot queries are the only Rust commands run by packaging. When packaging from Windows, `-Python C:/path/to/python.exe` selects Python explicitly.
|
||||||
|
|
||||||
The script refuses existing output/claim names. It creates `dist/ArcadiaStudio-<version>-x86_64-unknown-linux-gnu/`, a `.tar.gz`, and an adjacent `.sha256`. A failed attempt leaves its claim and any partial outputs for diagnosis; choose a fresh version or output root rather than overwriting them. Archives use sorted paths, zero timestamps and uid/gid, 0755 for the two Linux executables and 0644 for other files. Every entry is read back and compared against staging, including permissions. The same unchanged inputs on the same Python/zlib implementation produce the same bytes; this does not promise reproducible compiler output.
|
The script refuses existing output/claim names. It creates `dist/ArcadiaStudio-<version>-x86_64-unknown-linux-gnu/`, a `.tar.gz`, and an adjacent `.sha256`. A failed attempt leaves its claim and any partial outputs for diagnosis; choose a fresh version or output root rather than overwriting them. Archives use sorted paths, zero timestamps and uid/gid, 0755 for the three Linux executables and 0644 for other files. Every entry is read back and compared against staging, including permissions. The same unchanged inputs on the same Python/zlib implementation produce the same bytes; this does not promise reproducible compiler output.
|
||||||
|
|
||||||
## Contents and provenance
|
## Contents and provenance
|
||||||
|
|
||||||
Both executables, the MIT license, a package-specific README, complete tracked examples (including all water-pressure teaching/evidence files), deployment references and the reachable documentation closure are included. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts are preserved verbatim and their internal source-tree hyperlinks are outside that documentation check. Untracked private files, caches and unrelated harness history are not included merely because they exist on disk.
|
All three executables, the MIT license, a package-specific README, complete tracked examples (including all water-pressure teaching/evidence files), deployment references and the reachable documentation closure are included. Relative Markdown/reST/HTML file links are checked before archiving; upstream license texts are preserved verbatim and their internal source-tree hyperlinks are outside that documentation check. Untracked private files, caches and unrelated harness history are not included merely because they exist on disk.
|
||||||
|
|
||||||
`package.json` records input hashes and static ELF dependencies. `SHA256SUMS.txt` inventories every file except itself; the archive checksum covers that inventory too. `dependency-notices.json`, `THIRD-PARTY-NOTICES.txt` and `licenses/` use the same strict collector as Windows: exact target-filtered normal/build dependency graph, locked crate archives, actual source hashes, checksum-bound supplements and embedded fonts, Rust distribution notices and target-library hashes. Unknown sources, absent notices or changed supplements stop packaging. No Linux system libraries are bundled by this pipeline; their runtime distribution supplies them and its notices.
|
`package.json` records input hashes and static ELF dependencies. `SHA256SUMS.txt` inventories every file except itself; the archive checksum covers that inventory too. `dependency-notices.json`, `THIRD-PARTY-NOTICES.txt` and `licenses/` use the same strict collector as Windows: exact target-filtered normal/build dependency graph rooted at `oa-desktop`, `oa-server` and `oa-cli`, locked crate archives, actual source hashes, checksum-bound supplements and embedded fonts, Rust distribution notices and target-library hashes. Unknown sources, absent notices or changed supplements stop packaging. No Linux system libraries are bundled by this pipeline; their runtime distribution supplies them and its notices.
|
||||||
|
|
||||||
ELF preflight requires little-endian x86-64 executables/PIE, the glibc dynamic loader, no RPATH/RUNPATH and a reviewed set of direct libc/compiler-runtime imports. Unknown libraries require an explicit packaging design instead of being silently copied from a developer machine. Graphics/window/portal libraries loaded at runtime are outside that static import list.
|
ELF preflight requires little-endian x86-64 executables/PIE, the glibc dynamic loader, no RPATH/RUNPATH and a reviewed set of direct libc/compiler-runtime imports. Unknown libraries require an explicit packaging design instead of being silently copied from a developer machine. Graphics/window/portal libraries loaded at runtime are outside that static import list.
|
||||||
|
|
||||||
Optionally pass `-BuildEvidence <json-file>`, containing nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by `arcadia-studio` and `arcadia-server`. Hash strings must be lowercase SHA256. Target and binary hashes are checked; other fields remain explicit caller assertions. Omission records build provenance as unknown. Never label a development binary an optimized release because of its directory or package version.
|
Optionally pass `-BuildEvidence <json-file>`, containing nonempty `source_identity`, `profile`, `compiler`, exact `target`, and `binary_sha256` keyed by `arcadia-studio`, `arcadia-server` and `arcadia`. Hash strings must be lowercase SHA256. Target and binary hashes are checked; other fields remain explicit caller assertions. Omission records build provenance as unknown. Never label a development binary an optimized release because of its directory or package version.
|
||||||
|
|
||||||
## Native extracted-bundle checks
|
## Native extracted-bundle checks
|
||||||
|
|
||||||
Extract into a fresh user-writable Linux directory and run from an unrelated working directory. Verify SHA256SUMS, file permissions, `file`/`ldd` and actual desktop/runtime libraries. A typical Ubuntu runtime needs X11/XKB and Wayland clients, Vulkan loader/Mesa Vulkan drivers, and working session DBus plus an xdg-desktop-portal backend for file dialogs. A successful `ldd` alone is insufficient because these libraries can be loaded dynamically.
|
The source authoring CLI is `./arcadia`. Run `./arcadia --help` for `check`, `lint`, `stabilize` and `rename-alias`; each takes an explicit `.arcadia` entry file. Mutation commands preview by default and require `--write` to apply their candidate; `--json` produces a machine-readable report. Test writes on a separate project copy. CLI use does not require a display, a server or Rust. Include CLI check, preview, write and unchanged-repeat checks in extracted-bundle acceptance, alongside desktop and server checks.
|
||||||
|
|
||||||
|
Extract into a fresh user-writable Linux directory and run from an unrelated working directory. Verify SHA256SUMS, file permissions, `file`/`ldd` and actual desktop/runtime libraries. A typical Ubuntu runtime needs X11/XKB and Wayland clients and Vulkan loader/Mesa Vulkan drivers. Linux Open/Save uses the application's path-entry dialog and does not require an xdg-desktop-portal backend. A successful `ldd` alone is insufficient because graphics/window libraries can be loaded dynamically.
|
||||||
|
|
||||||
For a software Vulkan trial, first locate the installed lavapipe ICD; only if it exists, use:
|
For a software Vulkan trial, first locate the installed lavapipe ICD; only if it exists, use:
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ For a software Vulkan trial, first locate the installed lavapipe ICD; only if it
|
|||||||
WGPU_BACKEND=vulkan VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./arcadia-studio
|
WGPU_BACKEND=vulkan VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./arcadia-studio
|
||||||
```
|
```
|
||||||
|
|
||||||
A compatible native GPU may run without these variables. WSLg uses real Linux user space with a particular display environment; record the actual X11/Wayland backend and do not infer support for all Linux sessions from one trial. Exercise Home walkthrough, Help/search, native Open/Save dialogs, a multi-file project, diagram/source edits, Undo/Redo and reopening at normal/minimum window sizes. Compare complete saved model/source and inspect stderr, not merely whether a window appeared.
|
A compatible native GPU may run without these variables. WSLg uses real Linux user space with a particular display environment; record the actual X11/Wayland backend and do not infer support for all Linux sessions from one trial. Exercise Home walkthrough, Help/search, Linux Open/Save path entry, existing-file handling, SVG export overwrite confirmation, a multi-file project, diagram/source edits, Undo/Redo and reopening at normal/minimum window sizes. Compare complete saved model/source and inspect stderr, not merely whether a window appeared.
|
||||||
|
|
||||||
Run the server against a separate local data directory and unused loopback port. Follow [server configuration](server.md) for health, authentication, ETags, restart and backup/restore. Test create/import, a changed conditional update, unchanged no-op, stale/invalid rejection, and persistence after a clean restart. Record exact binary/archive/source/compiler hashes and platform. Packaging verification is separate from current-tree release acceptance.
|
Run the server against a separate local data directory and unused loopback port. Follow [server configuration](server.md) for health, authentication, ETags, restart and backup/restore. Test create/import, a changed conditional update, unchanged no-op, stale/invalid rejection, and persistence after a clean restart. Record exact binary/archive/source/compiler hashes and platform. Packaging verification is separate from current-tree release acceptance.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Source CLI
|
||||||
|
|
||||||
|
Use `arcadia` to check or update local `.arcadia` projects without opening Studio
|
||||||
|
or starting a server. Pass the entry file explicitly; its relative include paths
|
||||||
|
resolve from their containing source files.
|
||||||
|
|
||||||
|
Build with `cargo build --locked -p oa-cli --bin arcadia`. The resulting command
|
||||||
|
is `target/debug/arcadia.exe` on Windows or `target/debug/arcadia` on Linux unless
|
||||||
|
your Cargo configuration chooses another target directory. A workspace release
|
||||||
|
build also includes it. Use the platform's usual executable path prefix when
|
||||||
|
running these examples.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
arcadia check project.arcadia
|
||||||
|
arcadia check project.arcadia --json
|
||||||
|
arcadia stabilize project.arcadia
|
||||||
|
arcadia stabilize project.arcadia --write
|
||||||
|
arcadia rename-alias project.arcadia old_alias new_alias
|
||||||
|
arcadia rename-alias project.arcadia old_alias new_alias --write --json
|
||||||
|
```
|
||||||
|
|
||||||
|
`lint` is an alias for `check`. Checks accept incomplete engineering models with
|
||||||
|
warnings; syntax errors and structural violations fail. Located diagnostics
|
||||||
|
refer to the named file and coordinate space. Line and column numbers are
|
||||||
|
one-based and columns count Unicode scalar values; byte ranges are half-open
|
||||||
|
UTF-8 offsets.
|
||||||
|
|
||||||
|
`stabilize` previews missing persistent identities and any supported source
|
||||||
|
migration. `rename-alias` changes one existing global alias and its typed
|
||||||
|
references while preserving comments, strings and unrelated source. Pass alias
|
||||||
|
names without `@`. The command does not rename an element's display name.
|
||||||
|
|
||||||
|
Both editing commands preview by default. Human output writes unified file
|
||||||
|
diffs to stdout and diagnostics to stderr. `--json` writes one versioned report
|
||||||
|
with diagnostics, migration information, exact before/after text for every
|
||||||
|
changed file, diffs and the write result. Unchanged files are not included in
|
||||||
|
that report. They are still checked for conflicts before a write.
|
||||||
|
|
||||||
|
Add `--write` to save the candidate prepared by that invocation. Even an empty
|
||||||
|
diff checks the captured files and pending-save state before returning. A failed
|
||||||
|
write reports `written: false` and retains its proposed diff. This does not
|
||||||
|
guarantee rollback; the reported conflict or recovery outcome determines the
|
||||||
|
next step. A pending save
|
||||||
|
requires explicit recovery through the existing project recovery workflow;
|
||||||
|
the CLI does not recover it automatically.
|
||||||
|
|
||||||
|
Missing IDs in a separate earlier preview remain provisional. A later command
|
||||||
|
may allocate different IDs until you save them. This CLI does not persist a
|
||||||
|
review plan for later application. It does not canonicalize the whole project
|
||||||
|
or delete detached files. Source versions 3–7 use the core's supported migration
|
||||||
|
rules; invalid source is not rewritten.
|
||||||
|
|
||||||
|
| Exit code | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| 0 | Valid check or successful preview/write; warnings may remain |
|
||||||
|
| 1 | Invalid source or refused semantic alias change |
|
||||||
|
| 2 | Invalid command arguments |
|
||||||
|
| 3 | File/path, conflict, pending-save or output failure |
|
||||||
|
|
||||||
|
Admission bounds match the desktop: 256 files, include depth 32, 1024 include
|
||||||
|
edges, 64 MiB total UTF-8, 100,000 graph objects, 250,000 records and 500,000
|
||||||
|
ordered references. `arcadia --help` lists the commands and bounds. These limits
|
||||||
|
are not a claim of interactive performance at the maximum project size.
|
||||||
|
|
||||||
|
For the notation itself, start with [the text-first examples](../examples/text-first/README.md)
|
||||||
|
and the [language contract](language.rst).
|
||||||
@@ -278,3 +278,71 @@ This is a selected design under implementation, not a native acceptance claim.
|
|||||||
Native reverse scenario Apply selected a dependency and inserted five overlapping behavior placements into an unrelated architecture view. Core transition correctness did not make that presentation useful. Select the counterpart of the explicit request root and resolve its nearest scenario/process/chain/state owner. Without an explicit destination diagram, keep the exact core candidate and open Behavior; preserve unrelated architecture views. Retain ordinary architecture transition placement and honor explicit diagram choices.
|
Native reverse scenario Apply selected a dependency and inserted five overlapping behavior placements into an unrelated architecture view. Core transition correctness did not make that presentation useful. Select the counterpart of the explicit request root and resolve its nearest scenario/process/chain/state owner. Without an explicit destination diagram, keep the exact core candidate and open Behavior; preserve unrelated architecture views. Retain ordinary architecture transition placement and honor explicit diagram choices.
|
||||||
|
|
||||||
Automatically creating another architecture view would retain the wrong editing workspace and introduce unrequested model state. Merely navigating to Behavior after adding placements would conceal the presentation damage. Both alternatives are rejected. The selected correction retains one source transaction and Undo operation and guards stale canvas events. Revisit if mixed-root requests or realistic explicit destination workflows require a separate user choice; do not infer such a choice from dependency ordering. Test/build/native acceptance is recorded separately in release-behavior-transition-context-20260906.md.
|
Automatically creating another architecture view would retain the wrong editing workspace and introduce unrequested model state. Merely navigating to Behavior after adding placements would conceal the presentation damage. Both alternatives are rejected. The selected correction retains one source transaction and Undo operation and guards stale canvas events. Revisit if mixed-root requests or realistic explicit destination workflows require a separate user choice; do not infer such a choice from dependency ordering. Test/build/native acceptance is recorded separately in release-behavior-transition-context-20260906.md.
|
||||||
|
|
||||||
|
## Property baselines and explicit alternative cases — 2026-09-11
|
||||||
|
|
||||||
|
Problem: complete independent bindings repeat shared engineering assumptions in
|
||||||
|
every ordinary study case. The user requires practical text-only development and
|
||||||
|
reduced cognitive load for small and large models. Evaluator implementation
|
||||||
|
simplicity alone cannot justify that authoring cost.
|
||||||
|
|
||||||
|
Select property-owned base values/formulas and one-level case overrides as the next
|
||||||
|
active-study contract to formalize. Every evaluator caller explicitly requests
|
||||||
|
ModelBaseline or a particular Case identity; no active UI selection, unnamed
|
||||||
|
default case or case-to-case inheritance determines meaning. The baseline is the
|
||||||
|
model's explicit property values, not a mandatory extra record for entering the
|
||||||
|
first value. Inherited formulas read the requested effective case. An explicit
|
||||||
|
Unbound override shadows a known base; clearing an override uses the base.
|
||||||
|
|
||||||
|
The [executed comparison](release-analysis-context-experiment-20260911.md) passed
|
||||||
|
218 assertions. Twenty cases with20 fixed/two varying inputs used62 binding rows
|
||||||
|
under B versus480 under D, with identical results. Updating one shared assumption
|
||||||
|
required one B binding versus20 D bindings. Reusable predicates eliminated D's
|
||||||
|
threshold-edit disadvantage, so repeated formulas are not the reason for selection.
|
||||||
|
|
||||||
|
Retain an explicit complete independent freeze/export for intentionally separate
|
||||||
|
assumptions. It must capture effective values/formulas and the full exact definition
|
||||||
|
environment, including package pins; copying only case rows leaves hidden live
|
||||||
|
dependencies. Captured revisions and per-input origins must make propagation
|
||||||
|
visible. Effective dependency/cycle validation and stale result handling must cover
|
||||||
|
the whole requested context.
|
||||||
|
|
||||||
|
Reject compulsory complete cases for ordinary shared-assumption modeling; retain
|
||||||
|
their useful isolation as an explicit independent snapshot. Reject arbitrary
|
||||||
|
inheritance and UI-dependent evaluation. Do not import the tiny experiment
|
||||||
|
interpreter as production architecture. Exact check identity, type/operator
|
||||||
|
algebra, source syntax, library closure/compatibility, transitions and public
|
||||||
|
snapshot formats still require the coordinated precise contract. Current source7
|
||||||
|
does not implement this decision.
|
||||||
|
|
||||||
|
Revisit if complete text/native tasks expose unacceptable hidden propagation,
|
||||||
|
wrong-context edits or inability to preserve reproducible independent studies.
|
||||||
|
The comparison establishes semantic/text maintenance evidence, not human task
|
||||||
|
timings or full quantity/library integration acceptance.
|
||||||
|
|
||||||
|
## Scenario lifetime rendering integration — 2026-09-11
|
||||||
|
|
||||||
|
Codex-ytr83r /root. Adopt the [scoped lifecycle proposal](release-scenario-lifecycle-design-20260910.md)
|
||||||
|
as derived presentation over existing source-7 semantics. Always retain a neutral
|
||||||
|
identity header/editing guide; depict definite live intervals separately, with
|
||||||
|
original-occurrence Create capsules and Delete crosses. Alternative, optional,
|
||||||
|
loop and parallel operands inherit their enclosing entry independently and leave
|
||||||
|
an unresolved continuation after a local lifetime change. Reused scenarios remain
|
||||||
|
opaque. This does not execute guards or add model/source/history records.
|
||||||
|
|
||||||
|
Full-height ordinary spines fail to communicate creation/deletion; one global
|
||||||
|
earliest-birth/latest-death range would falsely order exclusive operands. Both
|
||||||
|
alternatives lose to the scoped projection. The [independent review](release-scenario-lifecycle-independent-review-20260911.md)
|
||||||
|
requires true visible root bounds, a complete rendering lane map and local
|
||||||
|
activation clipping at Delete; insertion bounds/eligible lanes must keep their
|
||||||
|
separate roles. Receiver endpoints and selectable markers must agree, including
|
||||||
|
self messages. Those findings determine the integration contract in language
|
||||||
|
section 8.1 and the real-Scene/native acceptance fixtures.
|
||||||
|
|
||||||
|
Core/source semantics are unchanged. In section 4.3, replacing the old
|
||||||
|
guaranteed-only lifetime/duplicate-reply wording with possible shared-path wording
|
||||||
|
corrects documentation to `validation.rs` and the existing conditional/parallel
|
||||||
|
regressions; it does not broaden admission or add a new rule. Compilation and
|
||||||
|
native acceptance of the renderer are still pending at this decision. Revisit the
|
||||||
|
visual encoding if actual minimum-size engineering workflows make marker identity,
|
||||||
|
uncertain continuation or the distinction between guide and existence unclear.
|
||||||
|
|||||||
@@ -1,83 +1,77 @@
|
|||||||
|
> Superseded on 2026-09-11. This document is a preserved historical checkpoint.
|
||||||
|
> Live plans, priorities, ownership and handoffs now reside in [Gitea issue 1](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-410) and [issue 2](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2).
|
||||||
|
> Do not resume synchronizing this ledger. See [STATE.md](STATE.md) for the integrated checkpoint.
|
||||||
|
|
||||||
# First release execution plan
|
# First release execution plan
|
||||||
|
|
||||||
Updated 2026-09-11, Codex-9e8r6t `/root`.
|
Updated 2026-09-11 for the published Codex-ytr83r B4 source checkpoint.
|
||||||
[PROJECT_GOAL.md](../PROJECT_GOAL.md) defines the outcome;
|
[PROJECT_GOAL.md](../PROJECT_GOAL.md) defines the full outcome;
|
||||||
[TASK_LEDGER.md](TASK_LEDGER.md) separates evidence from assumptions;
|
[STATE.md](STATE.md) scopes evidence/ownership and
|
||||||
[STATE.md](STATE.md) owns active leases and results.
|
[TASK_LEDGER.md](TASK_LEDGER.md) separates facts from decisions.
|
||||||
|
|
||||||
## Immediate implementation and acceptance
|
## Immediate integration and verification
|
||||||
|
|
||||||
1. Preserve the verified creation integration and matching GUI stage, committed
|
1. Preserve completed integration c81e285/e207b04/b853a1e and reviewed B4 source
|
||||||
as 4bb4aaf and transferred with independent anonymous readback/hash verification. Final immutable
|
52f6e17 on codex/release1-ytr83r. Do not replay equivalent3978/d114 changes.
|
||||||
desktop653/653 passed after creation ancestry/guide changes, alongside focused
|
CLI-only01b76a1 was reviewed and imported as023e3a8 without core/dependency
|
||||||
creation21/guide3. StrictClippy/fmt pass after a behavior-equivalent history
|
changes. Preserve the new AGENTS maintenance instruction and exact goal bytes.
|
||||||
rewrite; affected history63 and GUI build also pass. Preserve exact artifact
|
2. Core433, independent lifecycle-oracle admission, focused lifecycle22 and full
|
||||||
scopes and serialize future Cargo/source leases.
|
desktop675 passed on this host. Production freeze2908405 is unchanged by
|
||||||
2. Named process/scenario/machine creation, exact Undo/Redo and nine-file reopen
|
evidence checkpoint3ade397. GUI build, both-layer history/reopen and corrected
|
||||||
have passed at actual minimum size. Wide/minimum cancellation passed; SA draft
|
workspace strict Clippy passed. Finish the affected test and Unicode/native
|
||||||
crash recovery is active. Address observed blank canvases and prerequisite
|
workflows below. Do not repeat the passed suites without a
|
||||||
creation detours through the staged-source plan: source subform retains the
|
source change or new finding. Root serializes writers and records exact scope.
|
||||||
parent draft and final adoption atomically creates prerequisite and consumer.
|
3. Use the imported evidence2ec creation-oracle v2 for native Undo comparisons.
|
||||||
First slice covers Lifeline, process Work and machine owner, with semantic
|
Reject unknown/changed detached files while allowing exact files
|
||||||
first-action prompts. Independent review precedes final integration/native QA.
|
retained from an explicit pre-Undo origin. Preserve full Project and reachable
|
||||||
3. Operate the rebuilt app at minimum/normal sizes. Complete the saved basic
|
SourceBundle equality and original role identities through Redo/reopen.
|
||||||
supplementary move/camera/invalid-draft and minimum-size story; the main basic
|
4. Exercise current Windows UI at minimum/wide sizes: creation name/owner/viewpoint,
|
||||||
model and explicit transition passed full Project+SourceBundle, Undo/Redo and
|
one-step history, stale/raw-draft rejection, first Escape/modal priority,
|
||||||
reopen on candidate3 at wide size. Exercise new
|
recovery, clicked root/ALT action/observation/reuse, exact caller return,
|
||||||
scenario workspace, creation and caller return with real inputs and saved
|
same-size Fit and natural subsequent scrolling. Use independent semantic
|
||||||
model evidence, then correct and reverify concrete defects.
|
predictions, not only screenshots or serialization of observed outputs.
|
||||||
4. Integrate standalone B4 scenario lifetimes and the external independent
|
5. Review current external Gitea handoffs before importing staged prerequisites,
|
||||||
scenario contract without overwriting local layout work. Resolve source-derived
|
empty-state actions, contextual chooser or source CLI. Validate final combined
|
||||||
lifetime versus insertion bounds, activation-after-Delete and lane-map issues.
|
workflows and preserve explicit ownership/qualification semantics.
|
||||||
Close remaining
|
6. After actual B4 test/native acceptance, prepare a distinct runnable Windows
|
||||||
contextual operand, missing-source and state-naming workflow gaps identified
|
prerelease with current-toolchain notices and exact executable/source hashes.
|
||||||
by the current behavior audit and native study.
|
Existing MCP credentials have now completed a scoped branch push and remote
|
||||||
|
readback; release attachment upload still needs concrete-artifact verification.
|
||||||
|
Codex-9e8r6t retains the earlier named-authoring prerelease ownership.
|
||||||
|
7. Linux setup/downloads/builds/checking are paused by direct user instruction.
|
||||||
|
Wait for the user to resolve WSL2 networking and explicitly instruct resumption.
|
||||||
|
Preserve installed Ubuntu, exact VirtioProxy configuration and complete/partial
|
||||||
|
offline inputs. Networking remains unresolved; preparation is not Linux
|
||||||
|
acceptance. Follow the current WSL/offline records rather than old reboot plans.
|
||||||
|
8. Incorporate the owner clarification in issue2: process/scenario interaction
|
||||||
|
should use consistent direct canvas workflows while preserving domain
|
||||||
|
constraints. Codex-956atf owns the shared canvas design; B4 rendering and
|
||||||
|
prerequisite forms support it but do not close this requirement.
|
||||||
|
|
||||||
## Foundational semantic additions
|
## Capability milestones
|
||||||
|
|
||||||
The updated objective explicitly requires functional constraints/calculations,
|
| Outcome | Work and acceptance |
|
||||||
quantities/units and maintained model libraries. Current metadata/includes are
|
| --- | --- |
|
||||||
insufficient. This work runs independently of useful behavior improvements.
|
| Usable direct behavior/state engineering | Continuous ordered/explicit process authoring, clicked root/nested scenario insertion, exact reuse bindings/caller return, state naming/creation, recovery and source-preserving history. Complete B4 lifetime rendering with actual lifetime bounds, branch-local Delete activation clipping, incomplete lanes and self-arrow endpoints. |
|
||||||
|
| Precise engineering analysis | Refine property-owned baseline values/formulas plus explicit ModelBaseline/Case one-level overrides. Close quantity kind/unit/value, exact/approximate numerical policy, dependencies/cycles, false/missing/error, complete freeze and definition provenance. Implement coherent validated core/source/REST/GUI/history contracts. An exact-duration slice alone cannot satisfy the full goal. |
|
||||||
|
| Maintained reusable libraries | Resolve source-only normalization, pinned offline definitions/closure, provenance versus checked same-origin compatibility, source→local instance maps, explicit three-way updates/local divergence, subjectless analysis closure and OA/SA mounted-data transition policy. Demonstrate two projects/two instances, cosmetic and semantic upgrades, conflicts/detach and rollback. |
|
||||||
|
| Practical complete textual and graphical tasks | Measure small/large text-only creation, cross-file reference navigation, safe alias rename and lossless patch preview. Integrate source CLI and bounded contextual choices; actual keyboard/IME, repeated names, stale events, recovery and minimum geometry must pass. Keep offline guide/examples and normative source documentation aligned. |
|
||||||
|
| Complete engineering stories | Close OA↔SA transition/reconciliation, typed data/interfaces/state, artifacts, saved views, hosted workflows and tutorial tasks against the full current goal. Confirm parity and precise specification rather than inheriting historical exclusions. |
|
||||||
|
| Reproducible Windows/Linux release | Current tests/format/strict Clippy, reST/OpenAPI/migration/example gates; real GUI and REST transport/restart/ETag conflict acceptance; matching optimized desktop/server packages, complete notices/archive hashes and clean-runtime launch. Fresh independent requirements/domain/parity/architecture/UX/runtime review resolves material findings before release. |
|
||||||
|
|
||||||
The newest objective also requires complete uncluttered text-only workflows.
|
Milestones run adaptively where independent work helps. Analysis and libraries
|
||||||
Combine the local three-task usability audit with the other host's language/
|
share definition/identity assumptions and must converge before broad migrations.
|
||||||
identity/API design. Two concise current-grammar recipes now pass their bounded
|
Other-host slices remain coordinated through issue #1 and Wiki. Native/Cargo
|
||||||
REST identity/source/persistence experiment. Next evaluate
|
leases are host-local; source ownership and exact transfer heads are shared.
|
||||||
cross-file definitions/references, safe alias rename and identity-preserving
|
|
||||||
patch preview on repeated-name modular models. Do not equate shorter examples
|
|
||||||
or parser throughput with task usability, and do not silently change omissions.
|
|
||||||
|
|
||||||
- Compare real candidate semantics before large changes. Use primary Capella,
|
## Evaluator boundaries and replanning
|
||||||
SysML and quantity/unit evidence, with explicit compatibility limits.
|
|
||||||
- Falsify candidates through mass/power/latency budgets, mixed/affine units,
|
|
||||||
unknown/error/false results, dependency cycles, reference identity and bounded
|
|
||||||
evaluation. Separate descriptive existing text from executable declarations.
|
|
||||||
- Probe libraries through two projects/two instances, pinned offline content,
|
|
||||||
exports/dependency closure, bindings, distinct origin/local identities, updates
|
|
||||||
with local divergence, detachment and exact transactional rollback.
|
|
||||||
- Run a small pure-core experiment for the selected candidate. Independent
|
|
||||||
criticism must precede broad schema/source/API migration. Retain current typed
|
|
||||||
Project, Command and source-writer boundaries unless evidence requires change.
|
|
||||||
- Implement the ratified contracts across validation, visitors, deletion,
|
|
||||||
identity, OA/SA transitions, source/reST, schema/REST, ergonomic graphical
|
|
||||||
authoring and help. Add real examples and independent parity acceptance.
|
|
||||||
|
|
||||||
## Integration and release
|
Use authoritative source semantics, exact UUID/source preservation and actual
|
||||||
|
user workflows. Source-only experiments are not accepted new grammar; mounted
|
||||||
|
widget tests are not native readability; synthetic packaging checks are not
|
||||||
|
Linux runtime. Preserve old environmental failures as evidence without inventing
|
||||||
|
current blockers. Continue independent work when platform or transfer work waits.
|
||||||
|
|
||||||
- Publish coherent commits and verified bundle transfers through Gitea; keep
|
[Archived prior plans](history/2026-09-11-ytr83r-integration/README.md) preserve
|
||||||
concise host-scoped handoffs and Wiki. Do not overwrite another host's branch.
|
all previous milestone detail. Update this plan when a counterexample changes
|
||||||
- Refresh relevant source/schema/example/doc gates and run current core,
|
the right next action; do not lower completion criteria after substantial effort.
|
||||||
desktop/server and real REST transport/restart/ETag/draft-conflict workflows.
|
|
||||||
- Build matching optimized desktop/server packages. Regenerate complete notices
|
|
||||||
for actual inputs and verify archive contents, hashes and clean-runtime launch.
|
|
||||||
Historical ELF fixtures and build preparation do not replace execution.
|
|
||||||
- Obtain a viable Linux environment and run current GUI/server workflows there.
|
|
||||||
Supported WSL2 failed; no repeated rejected workaround. Continue independent
|
|
||||||
work while the environment question is pending.
|
|
||||||
- Conduct fresh independent requirements, semantic, usability, architecture,
|
|
||||||
correctness, runtime and documentation reviews. Resolve all material findings,
|
|
||||||
reverify affected behavior and only then declare release completion.
|
|
||||||
|
|
||||||
Historical milestones and evidence remain intact in
|
|
||||||
[the dated plan archive](history/2026-09-10-ledgers-before-second-consolidation/PLAN.md).
|
|
||||||
They do not supersede current user scope or imply current-tree acceptance.
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Current engineering work and evidence
|
||||||
|
|
||||||
|
Start with the [user goal](../PROJECT_GOAL.md), [current status](STATE.md),
|
||||||
|
[execution plan](PLAN.md), [task ledger](TASK_LEDGER.md) and
|
||||||
|
[decisions](DECISIONS.md). Follow [AGENTS.md](../AGENTS.md) and current
|
||||||
|
[Gitea coordination](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1)
|
||||||
|
before changing shared work. Dated reports describe their own source, host and
|
||||||
|
artifact; they are not current instructions or acceptance of later changes.
|
||||||
|
|
||||||
|
## Runnable milestones
|
||||||
|
|
||||||
|
- [Windows Studio and REST server authoring preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.authoring.20260911)
|
||||||
|
- [Standalone Windows source CLI preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/cli-v0.2.0-preview.authoring.20260911)
|
||||||
|
|
||||||
|
These are unfinished development milestones. Their release notes and validation
|
||||||
|
attachments define exactly what was shipped and tested. Full OA/SA engineering,
|
||||||
|
analysis, maintained libraries and current Linux acceptance remain unfinished.
|
||||||
|
|
||||||
|
## Current local evidence
|
||||||
|
|
||||||
|
| Question | Authoritative record |
|
||||||
|
| --- | --- |
|
||||||
|
| Which source was integrated? | [Integration audit](release-integration-ytr83r-20260911.md), [CLI import review](release-cli-integration-ytr83r-20260911.md) |
|
||||||
|
| What does lifetime rendering mean? | [Implementation and corrected review findings](release-lifecycle-render-ytr83r-20260911.md), [language specification](../docs/language.rst) |
|
||||||
|
| What will native behavior be compared against? | [Independent fixture and procedure](fixtures/scenario-lifecycle-native/README.md), [exact pre-native evidence](experiments/lifecycle-native-ytr83r-20260911/) |
|
||||||
|
| Which checks ran on this host? | [Host build record](release-host-bootstrap-ytr83r-20260911.md), [current status](STATE.md) |
|
||||||
|
| What remains for Windows packaging? | [Current runtime/notice preflight](release-b4-package-preflight-ytr83r-20260911.md), [publication path](release-publication-path-ytr83r-20260911.md) |
|
||||||
|
| Is Linux available? | [WSL outcomes and current user pause](release-wsl-postboot-ytr83r-20260911.md), [preserved offline inputs](release-linux-offline-preparation-ytr83r-20260911.md); setup/build/checking waits for the user's networking-resolution signal |
|
||||||
|
| What changed for Linux delivery? | [Three-binary CLI packaging and checks](release-linux-cli-packaging-ytr83r-20260911.md), [portable REST restart probe](release-rest-probe-portability-ytr83r-20260911.md); native execution remains pending |
|
||||||
|
| Which bounded follow-ups are proposals? | [Explicit Action allocation](release-qualified-action-design-ytr83r-20260911.md), [inheritance scaling diagnosis](release-inheritance-scaling-diagnosis-ytr83r-20260911.md) |
|
||||||
|
|
||||||
|
External staged-source, chooser and analysis/library owners maintain their current
|
||||||
|
handoffs in Gitea and the [shared Wiki](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/wiki/Home).
|
||||||
|
Import exact coherent source checkpoints before transferring conclusions to this checkout.
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
[Archived integration ledgers](history/2026-09-11-ytr83r-integration/README.md)
|
||||||
|
preserve source-specific facts and earlier ownership. Existing dated evidence and
|
||||||
|
Git history remain available. Preserve unique findings and update links before
|
||||||
|
moving or removing material; active issue #1 remains open for the unfinished goal.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Resume after the user-requested reboot — 2026-09-11
|
||||||
|
|
||||||
|
Codex-ytr83r /root. The complete first-release goal remains active and unfinished.
|
||||||
|
Read AGENTS.md, PROJECT_GOAL.md, current Gitea issues1/2 and Wiki before resuming.
|
||||||
|
This is a pause for the user's reboot, not a completed milestone or failed test.
|
||||||
|
|
||||||
|
## Saved source and evidence
|
||||||
|
|
||||||
|
Branch: codex/release1-ytr83r. Production source is B4 lifetime rendering52f6e17
|
||||||
|
plus CLI01b imported as023e3a8. b96286e records exact core/oracle evidence and
|
||||||
|
current harness index. d041212 preserves owner instruction-sync history and the
|
||||||
|
byte-exact supplied PROJECT_GOAL. a9a5768 records the Linux trial and contextual
|
||||||
|
source integration review. Later checkpoint commits add this resumption note.
|
||||||
|
|
||||||
|
Core all-targets passed433 tests, one intentionally ignored probe; the optional
|
||||||
|
probe separately passed. The independent lifecycle oracle and all six frozen
|
||||||
|
baseline/rename files passed before any native GUI launch. See the harness index.
|
||||||
|
No current desktop compile/test/native acceptance has been established.
|
||||||
|
|
||||||
|
## Interrupted Windows build
|
||||||
|
|
||||||
|
Cargo session93786 was interrupted at the user's request while compiling Iced
|
||||||
|
dependencies (last log entry cc1.4.5). It exited1 from interruption; no final gate
|
||||||
|
status JSON was emitted. A readback found no cargo.exe/rustc.exe process. Exact
|
||||||
|
partial log and explicit interruption record are in
|
||||||
|
experiments/lifecycle-native-ytr83r-20260911/. Preserve cached target outputs.
|
||||||
|
|
||||||
|
Use the existing ignored task-local toolchain, one Cargo job, after checking no
|
||||||
|
other writer is active. From the repository root in PowerShell:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& '.local\run-gate.ps1' -GateName 'ytr83r-desktop-lifecycle-resume' -CargoArgs @('test','--locked','--offline','-p','oa-desktop','scenario_lifecycle','--','--test-threads=1')
|
||||||
|
```
|
||||||
|
|
||||||
|
The wrapper loads .local/toolchain/env.ps1. Then run appropriate direct-canvas,
|
||||||
|
full/strict desktop, server/CLI and GUI build gates serially. Review the new
|
||||||
|
typography report: wide Unicode Create labels can overflow the fixed capsule.
|
||||||
|
The proposed clipping correction is not implemented; preserve existing fixtures
|
||||||
|
and define separate expected Unicode cases before native output. Actual Windows
|
||||||
|
wide/minimum/Fit/Save/Undo/Redo/reopen acceptance and B4 packaging remain pending.
|
||||||
|
|
||||||
|
## Linux after reboot
|
||||||
|
|
||||||
|
Recheck wsl --status and wsl --list --verbose. Before reboot, no distribution was
|
||||||
|
installed; WSL reported virtualization unavailable and Windows had a pending
|
||||||
|
reboot. Hardware virtualization must be available (firmware or nested VM host).
|
||||||
|
No host setting was changed and no reboot was performed by the agent.
|
||||||
|
|
||||||
|
Prefer a working WSL2 Ubuntu for real Linux build/runtime validation. The verified
|
||||||
|
Ubuntu image and QEMU cache remain under .local/linux-host-ytr83r. Its bounded
|
||||||
|
UEFI/TCG pilot ended without guest-ready evidence; all owned pilot processes
|
||||||
|
stopped. Do not repeat failed delivery or infer Linux acceptance from the image.
|
||||||
|
|
||||||
|
## Coordination and next integration
|
||||||
|
|
||||||
|
Codex-9e8r6t owns contextual prerequisites; df6f5956 is available locally and has
|
||||||
|
a reviewed minimal import path, with two B4-preserving merges. Later short-host
|
||||||
|
layout and Save-procedure corrections are separate; inspect the current remote
|
||||||
|
handoff before import. Codex-956atf owns chooser, shared constrained canvas design
|
||||||
|
for issue2, and analysis/library contracts. Local audit agents have stopped their
|
||||||
|
assigned work. They must recheck coordination before taking new tasks.
|
||||||
|
|
||||||
|
The two published Windows authoring/CLI prereleases remain available. No B4
|
||||||
|
release should be published until actual current tests/native acceptance pass.
|
||||||
@@ -1,202 +1,49 @@
|
|||||||
# Progress Ledger — first release
|
# Integrated checkpoint — 2026-09-11
|
||||||
|
|
||||||
Updated 2026-09-11, Codex-9e8r6t `/root`, Windows host.
|
Codex-ytr83r, branch `codex/release1-ytr83r`. The full [user goal](../PROJECT_GOAL.md) remains incomplete. Live integration/acceptance work is in [Gitea issue5](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/5); the [project overview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1) and [process/scenario requirement](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2) link other work. This file scopes integrated code and evidence.
|
||||||
**Release remains unaccepted; meaningful product and integration work is active.**
|
|
||||||
|
|
||||||
## Objective and current source
|
## Verified code and capabilities
|
||||||
|
|
||||||
Deliver the complete OA/SA first release in [PROJECT_GOAL.md](../PROJECT_GOAL.md).
|
Earlier published checkpoint `3ade397008cffe826d5f5a70e7c0233159e3bd42` contains production `29084050a7c020e0fb488be37c6e1ab235769cc0`; push and exact remote readback passed. It combines prior named behavior creation/history integration (`c81e285`, `e207b04`), corrected creation-oracle evidence (`b853a1e`), CLI-only integration (`023e3a8`) and B4 lifecycle rendering.
|
||||||
The latest authoritative attachment is
|
|
||||||
`815ff7b9-73c4-41a2-a809-635ecf8389d6/goal-objective.md`, read in full and preserved
|
|
||||||
in that file. It requires constraints/calculations, quantities/units and reusable
|
|
||||||
libraries, plus uncluttered human/AI text-only modeling at small and large scale.
|
|
||||||
Long homogeneous lists and poor behavior editing remain explicit concerns.
|
|
||||||
|
|
||||||
Local branch: `codex/release1-9e8r6t`, base `1797516`, committed head `4bb4aaf`.
|
B4 renders local lifetimes, branch-local activation termination, neutral incomplete lanes, Create capsules and Delete markers with original occurrence identity. Real Windows OA/SA six-case matrices and separate name-only Save/Undo/Redo/reopen workflows passed complete independent Project, reachable-source and physical-file comparisons. These are bounded fixture checks, not complete product usability acceptance.
|
||||||
Named behavior creation, its guide and independent oracle, concise text-first
|
|
||||||
examples and prior integration evidence are committed and remotely transferred.
|
|
||||||
Its final ancestry/guide correction passed full653; post-lint history63, strict
|
|
||||||
Clippy/fmt and matching GUI build passed. Current uncommitted work corrects the
|
|
||||||
oracle's retained-file rule, clarifies source-7 constructor defaults, records
|
|
||||||
native creation and implements contextual prerequisite creation. Preserve all
|
|
||||||
workers' edits. No open Gitea PRs at the latest read, comments through138.
|
|
||||||
|
|
||||||
## Completed work and scoped evidence
|
- [Native lifecycle observations and limits](release-lifecycle-native-ytr83r-20260911.md).
|
||||||
|
- [OA evidence archive](lifecycle-native-ui-oa-archive-20260911.md) and [SA evidence archive](lifecycle-native-ui-sa-archive-20260911.md).
|
||||||
|
- [Lifecycle implementation and independent findings](release-lifecycle-render-ytr83r-20260911.md).
|
||||||
|
- [Creation and source integration evidence](release-staged-source-integration-ytr83r-20260911.md).
|
||||||
|
|
||||||
- Palette inline naming and focused Escape: `7d1df4f`, independently published as
|
Source `321897fd55af9e07afa2c6ebf1e50be49e86f7d9` integrates the published contextual source-creation/minimum-card corrections from b2c3e540 with B4 and the header clipping correction. Its own Windows desktop all-targets gate passed 704 tests. [Exact selective integration, independent union review and Windows results](release-contextual-integration-update-ytr83r-20260911.md).
|
||||||
`codex/palette-authoring-20260910`. Candidate 2 focused tests, GUI build and
|
|
||||||
strict desktop Clippy passed. Its native F2 rename/Save/Undo/Redo matched exact
|
|
||||||
three-file expectations; Entity creation matched the complete independent
|
|
||||||
model/source oracle. Candidate3 completed ordinary Activity, existing placement,
|
|
||||||
allocation, direct Interaction and F2: complete basic model/four sources PASS.
|
|
||||||
Explicit SA actor/destination transition passed complete model/six sources;
|
|
||||||
Undo four-source baseline, Redo six-source result and reopen all passed. Six
|
|
||||||
physical files are byte-identical across reopen/reuse-preview Cancel/Save.
|
|
||||||
Immutable checkpoints/logs: `.local/native-qa/palette-20260910-v3/`.
|
|
||||||
- Recovery naming autofocus: `31b7dc3`, 13 focused checkpoint tests passed after
|
|
||||||
integrated compilation. Its new autofocus behavior still needs native check.
|
|
||||||
- Other-host behavior/topology/scenario/history: original `01bb52a` integrated
|
|
||||||
as `057eddd`; constructor cache fix `b6b76ef` integrated as `a70de55`; explicit
|
|
||||||
history seed fix `4ecad78` cherry-picked as `81668f9`.
|
|
||||||
- Corrected immutable desktop artifact completed **631/631 passed**,408.18s,
|
|
||||||
exit0; all three prior618-artifact failures are fixed. Exact artifact/logs in
|
|
||||||
`.local/scenario-workspace-20260910/tests-81668f9/`. It covers3978bbe workspace,
|
|
||||||
Back/OpenUse guards and both history fixes, but not later Sensor/creation work.
|
|
||||||
- A real same-size owner/document Sensor regression led to `d114a27`; affected
|
|
||||||
scenario53/53 passed. Current native candidate3 predates that correction.
|
|
||||||
Mounted workspace tests cover500×570 and wide geometry; native current-build
|
|
||||||
scenario acceptance remains pending.
|
|
||||||
- Named local creation passed final21/21 and guide3/3 after ancestry-label
|
|
||||||
correction. The matching immutable desktop suite passed **653/653**,362.83s,
|
|
||||||
zero failures/ignored; exact artifact864B0F…E86/log/result under
|
|
||||||
`.local/behavior-creation-20260911/tests-final`. Independent integration review
|
|
||||||
found no additional correctness blocker. StrictClippy/all-targets/-Dwarnings
|
|
||||||
and workspacefmt pass after one behavior-equivalent history_fields let-chain
|
|
||||||
correction; affected history63/63 and matching GUI rebuild pass. Current native
|
|
||||||
GUI is `gui-verified` SHA D4D9C2BD…B20C31. At actual 1100×700 client size,
|
|
||||||
named OA Process, explicit ParticipantInteraction Scenario with capability
|
|
||||||
context and SA owner-linked State machine plus requested first view passed
|
|
||||||
complete independent Project/SourceBundle checks at 7/8/9 files. Each single
|
|
||||||
Undo/Redo preserved exact expected semantics, sources and role identities.
|
|
||||||
Reopen/Save retained all nine physical source files byte-for-byte. Wide/minimum
|
|
||||||
draft cancellations retained the initial six files exactly. Detailed report:
|
|
||||||
[native creation](release-behavior-creation-native-20260911.md).
|
|
||||||
- Independent creation oracle v2 corrects a harness assumption: Save retains
|
|
||||||
known detached source files after Undo. Full Project and reachable source
|
|
||||||
equality remain mandatory; the complete physical inventory must equal the
|
|
||||||
explicit pre-Undo checkpoint overlaid with expected reachable bytes. Unknown,
|
|
||||||
changed or missing retained files fail. No product defect was hidden by this
|
|
||||||
correction; see [oracle review](release-behavior-creation-oracle-20260911.md).
|
|
||||||
- Fresh source-7 constructor probes passed 54 explicit/omitted comparisons and
|
|
||||||
12 required-field refusals across OA/SA, with complete typed models, canonical
|
|
||||||
reparse and exact authored no-op/name-edit retention. Constructor and Scenario
|
|
||||||
default wording now describes current behavior; strict HTML regeneration
|
|
||||||
passed. Other constructor families are explicitly outside this evidence.
|
|
||||||
See [default review](release-constructor-defaults-review-20260911.md).
|
|
||||||
- Source schema checks passed: 72 fixtures, 204 schemas, 14 families, 90 scalar
|
|
||||||
leaves. API/language reST rendered with strict docutils without warnings.
|
|
||||||
- Packaging committed `b98f135`: portable Linux adapter, shared package checks,
|
|
||||||
Docker input validation and exact upstream notices. Independent review found
|
|
||||||
and fixed five concrete defects. Python 11/11 and PowerShell contracts pass.
|
|
||||||
Historical Windows-input ZIP has 557 files; real Linux notices close over
|
|
||||||
339 packages/681 files. Linux archive adapter used inert ELF fixtures, not
|
|
||||||
actual current application binaries. Exact Fira/Cantarell notice Git bytes
|
|
||||||
were independently verified. Current-source notices need regeneration.
|
|
||||||
- Independent native Capella creation study completed, saved and closed cleanly;
|
|
||||||
report `release-capella-creation-study-20260910.md` is complete. Viewpoint-first
|
|
||||||
naming and distinct local create/existing selection inform the implementation.
|
|
||||||
- Textual usability audit completed three static tasks and example counts. The
|
|
||||||
existing grammar already permits omitted initial IDs/defaults; key gaps are
|
|
||||||
cross-file reference context, safe alias refactoring and reviewed patches.
|
|
||||||
Source7 EBNF/schema documentation and viewpoint omission guidance are corrected;
|
|
||||||
generated language HTML passed strict docutils. Both concise snippets passed
|
|
||||||
26 real REST requests: identity-only stabilization, exact no-op/name edit,
|
|
||||||
stale/invalid nonmutation and restart persistence. All120 current core/server
|
|
||||||
files match the preserved source7 binary's documented snapshot. This closes
|
|
||||||
the bounded recipe gap, not large-model or native text-editor acceptance. The
|
|
||||||
verified recipe is now `examples/text-first` and in the language reference.
|
|
||||||
|
|
||||||
Detailed evidence: [integrated gates](release-integrated-gates-20260910.md),
|
Published production `865483b4c66361a01fe0d9e731859220bafb4136` adds the reviewed child-work correction: first refinement transfers existing Allocation and RoleAssignment identities, metadata and artifact references to the child; later siblings inherit only a unique performer. OA/SA regressions cover nested children, roles, folded grouping, unallocated/mixed controls and exact history. The extracted Studio `BF9C2E61…` passed actual OA first-child, sibling and grandchild creation plus Save/Undo/Redo comparisons against original complete model/source checkpoints. Reopening and the SA native counterpart remain unverified. [Child-work fixture and oracle](fixtures/child-work-native/README.md); [native OA evidence](experiments/native-child-oa-865483b-20260911/README.md); [live acceptance boundary](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/4).
|
||||||
[palette native](release-palette-native-20260910.md),
|
|
||||||
[scenario workspace](release-scenario-workspace-20260910.md),
|
|
||||||
[creation design](release-local-behavior-creation-20260910.md),
|
|
||||||
[packaging implementation](release-packaging-implementation-20260910.md),
|
|
||||||
[independent packaging review](release-packaging-independent-review-20260910.md).
|
|
||||||
|
|
||||||
## Active ownership and execution
|
## Verification scope
|
||||||
|
|
||||||
- Root: integration, common ledgers, source/API checks and exclusive native input.
|
On this Windows host, core all-targets passed433 tests with one intentionally ignored scaling probe; that probe separately passed with unoptimized timings. Production29084050 passed focused lifecycle22 and full desktop675 tests (the22 are included in675), and the ordinary GUI build/launch. An equivalent test-helper let-chain correction subsequently passed strict workspace/all-targets Clippy and its focused regression1/1. These results predate the newly identified header-rendering correction.
|
||||||
Full653 is terminal PASS. V4 creation/Undo/Redo/reopen is complete; actual SA
|
|
||||||
pending-draft crash recovery is active on the same verified GUI. The forced
|
|
||||||
interruption targeted only isolated QA PID5184 after checkpoint persistence;
|
|
||||||
restarted PID1284/window6161106 uses the same private profile and runtime.
|
|
||||||
Recovery is now terminal PASS: full draft fields/IDs retained, autofocus,
|
|
||||||
first Escape and a fresh Save As preserve the complete nine-file model. The
|
|
||||||
current wide GUI is saved on `checkpoint-recovery-cancel/project.arcadia`;
|
|
||||||
treat that checkpoint as immutable and reopen a work copy before editing.
|
|
||||||
- `release_audit`: prior creation and constructor reports frozen. Now owns the
|
|
||||||
staged prerequisite creation implementation for Scenario Lifeline/process Work/
|
|
||||||
New machine owner, semantic first-action prompts, relevant App/recovery/guide
|
|
||||||
wiring and focused tests. Cargo writer is reserved to this worker when needed;
|
|
||||||
freeze source during tests and request independent review before integration.
|
|
||||||
- `behavior_audit`: v2 native oracle frozen, warnings-denied compilation and
|
|
||||||
falsifiers pass, executable056EEFD…F6A36. Now independently challenges staged
|
|
||||||
source semantics and expected behavior; no production/Cargo/native ownership.
|
|
||||||
- `palette_finish`: native report/evidence frozen: 13 creation oracle commands
|
|
||||||
and 19 separate recovery checks pass. Now owns new `crates/oa-cli` for local
|
|
||||||
source check/lint, preview/explicit-write stabilization and alias rename/diff.
|
|
||||||
Uses existing source-file diagnostics/expectations/journal writer, no grammar
|
|
||||||
or core changes. Root owns workspace/lock integration; serialize Cargo with
|
|
||||||
release_audit. Preview UUIDs remain candidates until that invocation writes.
|
|
||||||
- Separate host Codex-956atf: standalone scenario lifetime projection, independent
|
|
||||||
native scenario oracle, quantity/calculation design and library design. It has
|
|
||||||
its own cold toolchain/results. Its source-only reviews are not local passes.
|
|
||||||
|
|
||||||
Verified bundles for eb5161b and 4ecad78 are retained in
|
- [Exact current-host logs and inputs](experiments/lifecycle-native-ytr83r-20260911/).
|
||||||
`.local/integration-20260910`. Local refs `codex/behavior-snapshot-eb5161b` and
|
- [Test-only Clippy correction](lifecycle-clippy-correction-ytr83r-20260911.md).
|
||||||
`codex/history-seed-20260910` preserve exact commits. B4 lifetime projection and
|
- [Current-host toolchain setup](release-host-bootstrap-ytr83r-20260911.md).
|
||||||
its guide delta are not integrated yet; coordinate shared scene wiring. Review
|
- [Independent Windows gap review](release-windows-gap-review-ytr83r-20260911.md).
|
||||||
found insertion versus lifetime bounds, activation after branch-local Delete and
|
|
||||||
unassigned-lane map differences that must be addressed before acceptance.
|
|
||||||
|
|
||||||
## Unresolved requirements and gates
|
The closed 321897f Windows gate passed 704 desktop tests with zero failed/ignored/filtered; all 261 recorded build inputs remained unchanged. Matching server `ABA87A5F…` passed semantic HTTP checks (204 schemas, 19 values, 9 rejected invalid shapes) and the corrected REST probe's 25 requests. [Closed runtime evidence](experiments/windows-runtime-321897f-20260911/README.md) preserves both earlier source-version failures. [Earlier integration checkpoint evidence](experiments/contextual-lifecycle-integration-ytr83r-20260911/README.md) preserves server/CLI 68 across 7 targets, schema 72/204 after the missing-jsonschema correction, and the old GUI's wide-name passive Save comparison.
|
||||||
|
|
||||||
1. Finish native basic supplementary move/camera/focus/minimum matrix and current
|
The later 865483b Windows desktop gate passed **707 tests**, including the three focused child-work regressions, with zero failed/ignored/filtered. All 261 recorded build inputs stayed unchanged. The matching debug GUI build and strict **desktop-only** all-targets Clippy passed; child-work and staged-source checker compilation/admission also passed. [Closed gates and exact inputs](experiments/windows-gates-865483b-20260911/README.md). These checks do not establish the remaining native rendering or packaged acceptance.
|
||||||
scenario/process/state tasks. Main basic/transition/history/reopen passed at
|
|
||||||
wide size. Check raw drafts, recovery, exact source and real task completion.
|
|
||||||
2. Implement/review/natively verify contextual
|
|
||||||
missing-source and first-action flows. Named creation/history/reopen passed;
|
|
||||||
operand-editing and qualified Action allocation remain separate open work.
|
|
||||||
3. Constraints/calculations and quantity compatibility are currently descriptive
|
|
||||||
metadata; same-project includes are not maintained cross-project libraries.
|
|
||||||
Independent semantic designs and falsification are active before broad schema
|
|
||||||
changes. See [library review brief](release-library-candidate-review-20260910.md).
|
|
||||||
Other-host proposals expose cosmetic package revision/type compatibility,
|
|
||||||
subjectless analysis closure and text-only instance normalization decisions;
|
|
||||||
full reports and paired criticism are now available from verified c6d296a.
|
|
||||||
Its 218-assertion maintenance experiment supports explicit baseline values
|
|
||||||
with one-level Case overrides; complete freeze and effective-case evaluation
|
|
||||||
are required. Refined baseline/library contracts remain active externally.
|
|
||||||
4. Implement and evaluate the source-only CLI baseline, then compare language
|
|
||||||
improvements using both local usability and complete other-host proposals.
|
|
||||||
Current grammar+tooling, domain syntax, namespaces and portable identity
|
|
||||||
modules remain distinct candidates. Concise examples/CLI alone do not prove
|
|
||||||
uncluttered modular engineering authoring or safe large-model changes.
|
|
||||||
5. Run coherent current desktop/core/server, specification, examples, REST and
|
|
||||||
optimized packaging gates after integration. Prior passes remain scoped.
|
|
||||||
6. Current Linux runtime requires a viable environment: supported WSL2 import
|
|
||||||
failed once with HCS_E_HYPERV_NOT_INSTALLED; no distro registered. The user
|
|
||||||
has a pending environment question. No repeated denied QEMU workaround.
|
|
||||||
7. Fresh independent cross-disciplinary release review remains required after
|
|
||||||
actual workflow and semantic acceptance, with all material findings resolved.
|
|
||||||
|
|
||||||
## Handoff and publication
|
The extracted package's unchanged server `ABA87A5F…` passed the actual HTTP schema checks (204/19/9) and 25-request REST editing/persistence/restart probe with a restricted process PATH and unrelated working directory. Seven raw response pairs and exact source snapshots matched; all five test-owned server processes stopped. The first REST run's PowerShell-added PATH directory is preserved as historical evidence; the corrected restricted-PATH rerun is authoritative. [Packaged server evidence](experiments/packaged-server-865483b-20260911/README.md). This completes bounded server runtime acceptance, while the new Windows preview remains unpublished pending the separate GUI checks.
|
||||||
|
|
||||||
Use Gitea issue #1 and current Wiki to coordinate as **Codex-9e8r6t**. Read before
|
## Material limitations
|
||||||
resuming; preserve the other host's work. Direct Git push now fails authentication;
|
|
||||||
anonymous fetch and Gitea MCP still work. Integration3978bbe was published and
|
|
||||||
independently fetched/hash-verified as a Git bundle on
|
|
||||||
`codex/integration-transfer-20260910`; Wiki `Integration3978bbe` and issue#97 own
|
|
||||||
exact transfer instructions. Other host has integrated it and an equivalent
|
|
||||||
Sensor fix. Creation4bb4aaf was likewise published on
|
|
||||||
`codex/integration-transfer-20260911`, file
|
|
||||||
`harness/transfers/creation-4bb4aaf.bundle.b64`: decoded105022 bytes,
|
|
||||||
SHA256 `0DEFAE91A56430076C432758F923BC0675911DA9FA7588C9961FF15F47EAEBF6`,
|
|
||||||
prerequisite3978bbe. Anonymous Git readback, strict base64 length/hash check and
|
|
||||||
`git bundle verify` passed; issue#128 gives selective integration instructions.
|
|
||||||
Publish subsequent corrected oracle/native/default evidence similarly; preserve
|
|
||||||
the other host's later B4/reference chooser/design work and avoid duplicates.
|
|
||||||
|
|
||||||
Other-host c6d296a transfer is verified and imported as local
|
Native wide Latin/CJK checks found neutral lifeline header text overflowing its card; Create labels remained contained. The [header correction](lifecycle-header-overflow-correction-ytr83r-20260911.md) is included in the tested 865483b source, but matching corrected pixels and combining/emoji native appearance remain unverified. The old GUI's wide-name passive Save pass establishes identity/model/source/file equality, not corrected rendering. Complete combined native and packaged acceptance remain separate. During the OA reopen check, the Computer helper repeatedly failed with `window capture timed out: timed out waiting on channel`; fresh window selection and one clean helper restart did not recover capture. Native input stopped at the Open dialog with the accepted grandchild result already saved. This is a validation-tool failure, not evidence of a model failure or a successful reopen.
|
||||||
`codex/behavior-snapshot-c6d296a`: 334830 bytes,
|
|
||||||
SHA256 `526A80E2D32ED0E57A7B2372B5F02D4652E8751DFEBA81953C4F045225D6AACB`,
|
|
||||||
prerequisite1797516, from `codex/behavior-transfer-20260910` file
|
|
||||||
`harness/transfers/behavior-c6d296a.bundle.b64`. Selected analysis/library/text
|
|
||||||
reports, criticism, experiments and independent scenario oracle are restored;
|
|
||||||
51 evidence files compare byte-for-byte with Git blobs. No source/ledger merge.
|
|
||||||
Issue#136/#143 and refreshed Wiki Authoring20260911 record the handoff.
|
|
||||||
|
|
||||||
The old ledgers, including all historical platform and native evidence, were
|
Other chooser/canvas and text-navigation contributions still require their own integration and verification. Calculations, quantities/units, maintained libraries, practical large-model textual workflows, complete OA↔SA engineering parity, documentation and fresh cross-disciplinary review remain full-goal requirements. See the [analysis proposal](release-analysis-semantics-design-20260910.md), [library proposal](release-libraries-semantics-design-20260910.md), [text-authoring design](release-textual-authoring-design-20260911.md) and [decisions](DECISIONS.md) for attributed design evidence, not completed implementation claims.
|
||||||
archived byte-for-byte before this consolidation in
|
|
||||||
[history/2026-09-10-ledgers-before-second-consolidation](history/2026-09-10-ledgers-before-second-consolidation/STATE.md),
|
## Platform and published deliverables
|
||||||
with hashes.json. Their historical-current wording does not supersede this file.
|
|
||||||
|
**Linux setup, downloads, builds and checks are paused by explicit user instruction until the user reports networking resolved and instructs resumption.** Ubuntu26.04/WSL2 shell execution was observed; guest networking remained unavailable. Offline preparation was a workaround, not a network fix or Linux acceptance. Host VPN settings were not inspected or changed. [WSL evidence](release-wsl-postboot-ytr83r-20260911.md) and [preserved offline inputs](release-linux-offline-preparation-ytr83r-20260911.md) retain exact outcomes.
|
||||||
|
|
||||||
|
Peer Codex-9e8r6t published the separately verified [Windows contextual prerelease](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.contextual.20260911), source `b2c3e540`; [its publication handoff](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2#issuecomment-406) binds its artifacts and original-host checks. It is not a B4 release from this branch.
|
||||||
|
|
||||||
|
## Record navigation
|
||||||
|
|
||||||
|
The [one-time record migration](history/2026-09-11-ytr83r-record-migration/README.md) preserves the exact prior STATE ledger and marks PLAN/TASK_LEDGER superseded. [Earlier integration history](history/2026-09-11-ytr83r-integration/README.md) preserves source-attributed older results. Historical leases, failed approaches and next-step wording do not override current Gitea coordination or the Linux pause.
|
||||||
|
|||||||
@@ -1,128 +1,145 @@
|
|||||||
|
> Superseded on 2026-09-11. This document is a preserved historical checkpoint.
|
||||||
|
> Live plans, priorities, ownership and handoffs now reside in [Gitea issue 1](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-410) and [issue 2](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/2).
|
||||||
|
> Do not resume synchronizing this ledger. See [STATE.md](STATE.md) for the integrated checkpoint.
|
||||||
|
|
||||||
# Task Ledger — first release
|
# Task Ledger — first release
|
||||||
|
|
||||||
Updated 2026-09-11 by Codex-9e8r6t `/root`. Release not accepted.
|
Updated 2026-09-11 for Codex-ytr83r. The full
|
||||||
|
[user goal](../PROJECT_GOAL.md) and [current progress](STATE.md) remain authoritative.
|
||||||
|
[Exact host ledger archives](history/2026-09-11-ytr83r-integration/README.md)
|
||||||
|
preserve prior facts, dates and reasoning without transferring their acceptance.
|
||||||
|
|
||||||
## Goal and success criteria
|
## Goal and given requirements
|
||||||
|
|
||||||
[PROJECT_GOAL.md](../PROJECT_GOAL.md), copied from attachment
|
Deliver a complete, usable OA/SA release on Windows and Linux, with one explicit
|
||||||
`815ff7b9-73c4-41a2-a809-635ecf8389d6/goal-objective.md`, is authoritative.
|
reST semantic contract and equivalent graphical, textual and REST access. Include
|
||||||
Deliver a usable, substantially complete OA/SA systems modeling product in
|
constraints/calculations, quantities/units, maintained libraries, behavior/state,
|
||||||
Rust/Iced on Windows and Linux, with equivalent graphical/textual/REST editing,
|
traceability and navigable offline help. Reduce cognitive load and visual clutter;
|
||||||
strict reST semantics/API, arbitrary development-artifact traceability and
|
small and large models must be practical using text alone. Later Arcadia layers
|
||||||
navigable offline help. Constraints/calculations, quantities/units and reusable
|
remain future scope. Compilation or a pleasant happy path is not completion.
|
||||||
model libraries and complete uncluttered text-only small/large modeling are
|
Issue2 further requires consistent direct process/scenario canvas interaction;
|
||||||
explicit requirements. Later Arcadia layers remain future
|
the owner's clarification allows domain constraints rather than arbitrary movement.
|
||||||
releases. Completion requires real engineering tasks, sound semantics, relevant
|
|
||||||
passing checks and fresh independent cross-disciplinary review.
|
|
||||||
|
|
||||||
## Given facts
|
At15:28UTC on2026-09-11, the user explicitly deferred Linux builds and checking
|
||||||
|
until they resolve WSL2 networking and instruct resumption. Linux setup/downloads
|
||||||
- User prioritizes Capella's domain capability and useful visual nesting while
|
are paused with inputs preserved. Continue independent Windows work; do not treat
|
||||||
rejecting clutter, intrusive creation rows, static behavior diagrams and long
|
offline preparation as a networking fix or Linux acceptance.
|
||||||
homogeneous option lists. Reduce cognitive load in small and large models.
|
|
||||||
- Source must be understandable to engineers and AI, work with Git and preserve
|
|
||||||
full parity. OA/SA transitions in both directions must be clear and reviewed.
|
|
||||||
- User authorizes autonomous reversible work, research, native Computer Use and
|
|
||||||
parallel agents. Use GPT-6 Astra exclusively with task-appropriate effort.
|
|
||||||
- Gitea coordination and shared Wiki updates are required. Identify host and
|
|
||||||
worker consistently and preserve overlapping work. Explicit project messages
|
|
||||||
are authorized; unrelated external messages are not.
|
|
||||||
|
|
||||||
## Verified facts
|
## Verified facts
|
||||||
|
|
||||||
[STATE.md](STATE.md) records exact commits, live leases and scoped verification.
|
- Local start was cleanff615d0. Root decoded/hash-verified/imported published3978,
|
||||||
The current model has one typed core, validated atomic commands and coordinated
|
c6 and4bb bundles; merged c6 as c81e285 and integrated creation4bb at e207b04. There were
|
||||||
Project/SourceBundle history shared by desktop and REST. Included files share
|
no production cherry-pick conflicts; docs/attributes require evidence union.
|
||||||
one global namespace. Current Constraint expressions are strings; numeric unit
|
The current goal is copied from the user's latest file.
|
||||||
strings do not convert values or establish dimensional compatibility. There is
|
- Read-only integration comparison finds equivalent workspace/Sensor code in c6;
|
||||||
no package/version/instance/update contract for cross-project libraries.
|
replaying3978 or d114 is unnecessary. Core source is unchanged betweenff andc6.
|
||||||
|
B4 now has a production Scene bridge and independently reviewed source, with
|
||||||
|
focused lifecycle22/22 and full desktop675/675 passed on source2908405. The
|
||||||
|
ordinary GUI build and real OA/SA lifecycle matrices now ran; OA full name-only
|
||||||
|
Save/Undo/Redo/reopen and the corresponding SA workflow passed exact independent
|
||||||
|
comparisons. Corrected workspace/all-targets strict Clippy also passed0;
|
||||||
|
Unicode appearance remains open. See the
|
||||||
|
[native record](release-lifecycle-native-ytr83r-20260911.md) for exact scope.
|
||||||
|
- Imported source implements named behavior creation, exact prepared-batch
|
||||||
|
correlation, success-only selection, raw-draft guards, recoverable original
|
||||||
|
layer and modal-priority Escape. These are source facts; combined local runtime
|
||||||
|
acceptance is pending.
|
||||||
|
- Earlier-host desktop642 and653 suites, scoped native scenarios/creation,
|
||||||
|
packaging25 notices and source experiments retain exact attribution in
|
||||||
|
[STATE.md](STATE.md). They are not new local passes.
|
||||||
|
- The original transferred creation oracle incorrectly equates physical disk files
|
||||||
|
with reachable sources during Undo. Later external v2 corrects the evaluator
|
||||||
|
with explicit origin/retained-byte rules; exact v2/evidence2ec was imported at
|
||||||
|
b853a1e after complete path/hash preflight. New host execution is still pending.
|
||||||
|
- Isolated Rust1.98.1/LLVM23.1.1 and locked dependencies are available. Packaging
|
||||||
|
11/11 and synthetic Windows notice checks pass. Current core all-targets passed
|
||||||
|
433 tests; its optional inheritance probe separately passed with slow debug
|
||||||
|
timings that require controlled optimized interpretation. Focused lifecycle
|
||||||
|
passed22/22 after two installed-Iced API corrections; full desktop passed675/675.
|
||||||
|
Strict reST/HTML generation passes with Docutils0.22.2.
|
||||||
|
- B4 independent criticism found and resolved self-caption overlap and two dense
|
||||||
|
minimum-Fit hit-selection counterexamples. Shared nearest marker/message-stroke
|
||||||
|
arbitration preserves exact marker/header/label selection. Ten real-Scene tests
|
||||||
|
and two clip geometry tests passed in the22-test focused gate. The independent twelve-scenario native oracle passed compile,
|
||||||
|
admission, self-check and exact baseline/name-only predictions before GUI output.
|
||||||
|
- The separate baseline-context experiment passed218 assertions; explicit
|
||||||
|
one-level overrides reduce repeated stable assumptions while complete frozen
|
||||||
|
definition environments remain necessary. Proposed syntax is not source7.
|
||||||
|
- After the user's virtualization/reboot change, supported Ubuntu26.04 install
|
||||||
|
completed exit0 and a real x86_64 WSL2 kernel shell completed. Later VM startups
|
||||||
|
timed out before apt. Owner328's exact VirtioProxy configuration now supports
|
||||||
|
a persistent root shell, but requested IP/DNS/HTTPS checks failed. Signed offline
|
||||||
|
dependency preparation advances without host VPN inspection or changes. This
|
||||||
|
establishes guest execution, not Linux builds or GUI acceptance; exact outcomes are
|
||||||
|
in the [WSL record](release-wsl-postboot-ytr83r-20260911.md).
|
||||||
|
|
||||||
Candidate3 completed the main basic/transition/history/reopen story with exact
|
## Assumptions and unresolved questions
|
||||||
four/six-source independent model oracles; supplementary native cases remain.
|
|
||||||
Corrected immutable desktop631/631 passed; later Sensor correction passed53/53.
|
|
||||||
Final creation21/guide3 and matching full desktop653/653 passed after ancestry
|
|
||||||
label correction. StrictClippy/fmt pass after one behavior-equivalent history
|
|
||||||
let-chain rewrite; affected history63 and matching GUI build pass. Native named
|
|
||||||
creation passes complete model and 7/8/9-source oracles, individual Undo/Redo and
|
|
||||||
byte-exact nine-file reopen at actual minimum size. Cancellation preserves the
|
|
||||||
six-file baseline. Blank process/scenario canvases provide no useful first-action
|
|
||||||
prompt; this is an observed usability finding. SA draft recovery remains active. Mounted
|
|
||||||
layout tests establish pane geometry, not native task acceptance. Packaging checks
|
|
||||||
and independent review are scoped to supplied binaries/fixtures, not release
|
|
||||||
acceptance. Supported WSL2 import failed; a current Linux runtime is unavailable.
|
|
||||||
|
|
||||||
Current grammar supports omitted initial IDs/defaults, lexical ownership, forward
|
- Combining already coherent desktop slices should preserve behavior; fresh build,
|
||||||
references and preserved source. Static audit found no exposed project-wide
|
automated and native gates must test that assumption.
|
||||||
reference navigation/alias refactor; current Find searches the active buffer.
|
- The baseline/library designs can fit existing atomic Project/Command/source
|
||||||
The process example has128lines/14UUIDs/31empty-default lines, while meaningful
|
boundaries; exact references, deletion, transition, imports, freezing and
|
||||||
shorter source is possible. Two concise snippets passed26 real REST requests,
|
numeric rules still need ratified contracts and falsification.
|
||||||
including identity-only normalization, exact name-only edits, rejected writes and
|
- Pinned package provenance must be distinguished from checked type compatibility;
|
||||||
restart persistence. Counts and this small trial prove neither improved native
|
unchanged quantity kinds cannot become incompatible merely through cosmetic
|
||||||
usability nor large-project performance.
|
repackaging without a deliberate policy. Energy and Torque remain distinct.
|
||||||
|
- Ubuntu26.04/WSL2 is the current supported Linux route; reliable repeat startup,
|
||||||
|
guest toolchain/dependencies and GUI/service/package execution remain open.
|
||||||
|
Initial shell success does not erase later startup timeouts. Old QEMU work is
|
||||||
|
retained as history, not the next route to repeat.
|
||||||
|
- Large-model human/AI task usability, cross-file refactoring, actual chooser
|
||||||
|
latency/keyboard/minimum geometry and complete native lifecycle behavior remain
|
||||||
|
to be measured. Parser admission or a widget experiment is insufficient.
|
||||||
|
|
||||||
## Assumptions
|
## Candidate designs and selected directions
|
||||||
|
|
||||||
- Contextual named creation, remaining-height canvas and local source insertion
|
The imported [analysis proposal](release-analysis-semantics-design-20260910.md),
|
||||||
can reduce actual authoring detours. Native engineer tasks must establish this.
|
[library proposal](release-libraries-semantics-design-20260910.md),
|
||||||
- A bounded pure calculation/quantity model and pinned library content may fit
|
[paired critique](release-semantic-proposals-review-20260910.md) and
|
||||||
the existing typed core better than a general-purpose metamodel rewrite.
|
[textual authoring design](release-textual-authoring-design-20260911.md) retain
|
||||||
Independent designs and experiments must test this before ratification.
|
alternatives and counterexamples. Metadata-only analysis and bare source includes
|
||||||
- Existing OA/SA semantics provide a useful foundation; neither their presence
|
do not meet evaluated quantities or maintained libraries.
|
||||||
nor old acceptance proves full conformance to the updated release objective.
|
|
||||||
|
|
||||||
## Open questions
|
Codex-956atf root selected property-owned baseline values/formulas with explicit
|
||||||
|
ModelBaseline/Case evaluation and one-level case overrides for contract refinement.
|
||||||
|
No hidden default case or UI-dependent evaluation; inherited formulas use the
|
||||||
|
effective requested case, and complete freeze captures definitions/provenance.
|
||||||
|
Exact numeric, library normalization/type compatibility and transition details
|
||||||
|
remain open. This direction is recorded in [DECISIONS.md](DECISIONS.md), not silently
|
||||||
|
treated as completed production semantics.
|
||||||
|
|
||||||
- Which precise calculation, quantity-kind/unit and library contracts satisfy
|
The external staged prerequisite design reserves source/consumer identities,
|
||||||
practical budgets, reuse and update workflows without ambiguous defaults or
|
keeps the parent context/rawtext/slot, stages locally and creates source+consumer
|
||||||
implicit execution? How should they migrate and participate in transitions?
|
in one atomic final command. It must not implicitly allocate work, involve
|
||||||
- Which behavior workflows still force cancellation/navigation to create missing
|
capabilities, retarget an Exchange-fixed endpoint or accept a different editor.
|
||||||
context, and which local actions best remove those detours?
|
Qualified Action allocation remains a distinct engineering choice.
|
||||||
- Which text-only assistance best preserves engineering context and identity:
|
|
||||||
native language services, external-editor services, source recipes or explicit
|
|
||||||
reviewed patch operations? Which grammar changes are actually necessary?
|
|
||||||
- Does the new scenario pane remain readable and editable through actual native
|
|
||||||
window sizes, camera changes, nested fragments and reopened caller contexts?
|
|
||||||
- Which viable Linux environment can run current GUI/server acceptance? Current
|
|
||||||
VM limitation does not block remaining Windows, semantics or source work.
|
|
||||||
|
|
||||||
## Candidate designs and decisions
|
## Retained implementation decisions
|
||||||
|
|
||||||
- Behavior creation: compact New → three task choices → focused name/owner/
|
- Keep one authoritative typed Project, validated atomic Commands, stable
|
||||||
viewpoint card, explicit missing inputs, atomic creation and reserved recovery
|
identities and explicit references. Semantic ownership/allocation/order and
|
||||||
IDs. Implemented, reviewed and scoped native creation/history/reopen passed.
|
diagram geometry remain distinct; forms are adapters, not a second metamodel.
|
||||||
- Contextual prerequisite creation: stage an explicit typed source inside the
|
- Preserve source ownership/comments/includes and exact model/source history.
|
||||||
current parent draft, then validate/adopt source and consumer atomically. This
|
Writeback succeeds before adoption. Invalid/rejected raw drafts survive and
|
||||||
preserves intent and one-step history; a separate Architecture excursion or
|
browsing/tool selection does not silently accept another editor. Recovery
|
||||||
early source commit loses those guarantees. Initial Lifeline/process Work/
|
validates/replans rather than trusting serialized commands.
|
||||||
machine-owner adapters and semantic first-action prompts are being implemented
|
- Semantic reconciliation uses finite typed facets, frozen baselines, explicit
|
||||||
and independently challenged; acceptance remains pending. See the
|
mappings and one atomic candidate. Names alone never establish identity.
|
||||||
[bounded plan](release-contextual-source-creation-plan-20260911.md).
|
- OA↔SA transitions are explicit engineering decisions. New persistent semantics
|
||||||
- Scenario workspace: compact header, optional Tools/Selection shelves, actual
|
need validation/reference/deletion/transition/source/API/UI/history parity.
|
||||||
remaining-height canvas/form, pinned actions, independent scrolling and
|
- Exact evidence bytes and host attribution survive integration. No focused
|
||||||
explicit Fit/Focus. Mounted tests support this choice; native proof pending.
|
test total is added to a full-suite count; old binary acceptance does not
|
||||||
- Quantities/calculations: compare additive pure analysis records, extension of
|
transfer to newly merged code.
|
||||||
existing fields and external evaluation. No broad schema decision yet.
|
|
||||||
- Libraries: compare copied source, live references, generic definition/usages
|
|
||||||
and pinned typed packages with explicit mappings and reviewed updates.
|
|
||||||
[Falsification brief](release-library-candidate-review-20260910.md) records
|
|
||||||
identity, dependency, offline and conflict requirements. No ratified winner.
|
|
||||||
- Text authoring: use existing omission/default/identity contracts for concise
|
|
||||||
experiments; compare source navigation/refactoring/patch assistance before
|
|
||||||
grammar redesign. No namespace, hidden identity or omission change ratified.
|
|
||||||
|
|
||||||
Long-lived decisions remain in [DECISIONS.md](DECISIONS.md). New user scope and
|
## Current strategy and coordination
|
||||||
normative contracts supersede older prototype exclusions.
|
|
||||||
|
|
||||||
## Current strategy
|
Root owns local integration, serialized builds, native inputs and final synthesis.
|
||||||
|
The local audit resolves evidence and reviews source; independent server review
|
||||||
|
has its own report. Codex-9e8r6t owns staged sources/first actions and source CLI;
|
||||||
|
Codex-956atf owns chooser, semantic contracts and the shared canvas design. Read Gitea issues #1/#2 and Wiki before
|
||||||
|
resuming, obtain exact coherent transfer heads, and respect their source ownership.
|
||||||
|
|
||||||
Finish coherent creation gates and test the rebuilt product against independent
|
First finish and verify the integrated current source using the imported corrected
|
||||||
saved-source oracles. Close remaining scenario lifecycle integration boundaries.
|
evaluator and newly prepared independent lifetime oracle. Then close remaining modeling/textual,
|
||||||
Use the native Capella study and independent criticism to address real authoring
|
analysis/library and platform gates with independent criticism and whole-task
|
||||||
friction. In parallel, resolve foundational semantic additions and text-only
|
acceptance. Update facts, assumptions and decisions as evidence changes them.
|
||||||
authoring through bounded competing designs and executable experiments. Then implement all representations,
|
|
||||||
verify real REST and Windows/Linux workflows, build actual optimized packages
|
|
||||||
and conduct fresh end-state review. Do not weaken acceptance to match current
|
|
||||||
implementation or stop because a narrow technical milestone passed.
|
|
||||||
|
|
||||||
Pre-consolidation content is preserved byte-for-byte in
|
|
||||||
[the dated archive](history/2026-09-10-ledgers-before-second-consolidation/TASK_LEDGER.md).
|
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
//! Native issue4 fixture/oracle. generate DIR | check oa|sa STAGE ENTRY
|
||||||
|
//! snapshot ENTRY DIR | compare ENTRY DIR | self-check
|
||||||
|
use oa_core::{source_project::*, *};
|
||||||
|
use std::{collections::{BTreeMap, BTreeSet}, fs, path::Path};
|
||||||
|
fn id(n: u128) -> Id { Id::from_u128(0xc1140000000040008000000000000000 + n) }
|
||||||
|
fn work(layer: Layer) -> Kind { if layer == Layer::Oa { Kind::Activity } else { Kind::Function } }
|
||||||
|
fn fixture(layer: Layer) -> Project {
|
||||||
|
let mut p = Project::empty();
|
||||||
|
p.id = id(1); p.alias = "child_work_native".into(); p.name = format!("Issue4 {layer:?} child work");
|
||||||
|
for (n, kind, name) in [(10, if layer == Layer::Oa { Kind::Entity } else { Kind::System }, "Performer"), (11, work(layer), "Parent work")] {
|
||||||
|
p.elements.insert(id(n), Element { id: id(n), alias: format!("seed_{n}"), name: name.into(), description: format!("Preserve original {name}"), parent: None, body: ElementBody::new(kind, layer).unwrap() });
|
||||||
|
}
|
||||||
|
p.relations.insert(id(20), Relation { id:id(20), alias:"original_allocation".into(), name:"Original responsibility".into(), description:"Preserve allocation engineering rationale 雨".into(), body:RelationBody::Allocation { source:id(10), target:id(11) } });
|
||||||
|
if layer == Layer::Oa {
|
||||||
|
for n in [12,13] {
|
||||||
|
p.elements.insert(id(n), Element { id:id(n), alias:format!("role_{n}"), name:format!("Responsibility role {n}"), description:"Preserve role".into(), parent:None, body:ElementBody::new(Kind::Role, layer).unwrap() });
|
||||||
|
for (r, kind, source, target) in [(n+10,RelationKind::RoleAssignment,id(n),id(11)),(n+20,RelationKind::RoleRealization,id(10),id(n))] {
|
||||||
|
p.relations.insert(id(r), Relation { id:id(r), alias:format!("responsibility_{r}"), name:format!("Role link {r}"), description:format!("Retain role link metadata {r}"), body:RelationBody::new(kind,source,target) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, owner) in [id(20),id(22),id(23)].into_iter().filter(|owner| p.relations.contains_key(owner)).enumerate() {
|
||||||
|
let key=id(50+index as u128);
|
||||||
|
p.artifact_links.insert(key, ArtifactLink { id:key, alias:format!("evidence_{index}"), name:format!("Responsibility evidence {index}"), description:"Keep owner identity and evidence metadata".into(), owner, relation:ArtifactRelation::References, uri:format!("https://example.invalid/evidence/{index}"), revision:Some("review-7".into()), fragment:Some("assignment".into()) });
|
||||||
|
}
|
||||||
|
p.diagrams.insert(id(2), Diagram { id:id(2), alias:"allocation_view".into(), name:format!("{layer:?} allocated work"), description:"Native child-work fixture".into(), layer, projection:ProjectionKind::Allocation, routes:vec![], view_options:Default::default(), nodes:BTreeMap::new() });
|
||||||
|
for (n,element,x,y,collapsed) in [(40,id(10),60.,60.,false),(41,id(11),84.,124.,true)] {
|
||||||
|
p.diagrams.get_mut(&id(2)).unwrap().nodes.insert(id(n), NodePlacement { id:id(n), alias:format!("seed_view_{n}"), element, position:Position{x,y}, size:Size{width:224.,height:94.}, collapsed, port_anchors:vec![] });
|
||||||
|
}
|
||||||
|
execute_batch(&p,&[]).unwrap()
|
||||||
|
}
|
||||||
|
fn bundle(p:&Project)->CompiledSourceProject {
|
||||||
|
let source=language::format(p).replacen("{\n","{\n include \"rationale.arcadia\";\n",1);
|
||||||
|
let raw=SourceBundle { entry:"project.arcadia".into(), files:BTreeMap::from([("project.arcadia".into(),source),("rationale.arcadia".into(),"// Preserve included note bytes: responsibility 雨.\r\n".into())]) };
|
||||||
|
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
|
||||||
|
assert_eq!(result.bundle,raw); assert_eq!(&result.decoded.project,p); result
|
||||||
|
}
|
||||||
|
fn load(entry:&Path)->CompiledSourceProject {
|
||||||
|
let raw=source_files::load_project(entry,ProjectLimits::default()).unwrap().reachable_bundle();
|
||||||
|
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
|
||||||
|
assert_eq!(result.bundle,raw,"No source normalization permitted");
|
||||||
|
execute_batch(&result.decoded.project,&[]).unwrap(); result
|
||||||
|
}
|
||||||
|
fn advance(base:&mut CompiledSourceProject,commands:Vec<Command>) {
|
||||||
|
let expected=execute_batch(&base.decoded.project,&commands).unwrap();
|
||||||
|
*base=update_bundle(base,&expected,ProjectLimits::default()).unwrap();
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Keys { children:[Id;3], nodes:[Id;3], allocation:Id }
|
||||||
|
fn expected(layer:Layer,stage:usize,k:Keys)->CompiledSourceProject {
|
||||||
|
assert!(stage<=3);
|
||||||
|
let mut base=bundle(&fixture(layer));
|
||||||
|
for step in 0..stage {
|
||||||
|
let p=&base.decoded.project;
|
||||||
|
let key=k.children[step];
|
||||||
|
let name=format!("New {}",work(layer).label().to_lowercase());
|
||||||
|
let element=Element { id:key, alias:p.fresh_alias(&name,"element",key), name, description:String::new(), parent:Some(if step==2 { k.children[0] } else { id(11) }), body:ElementBody::new(work(layer),layer).unwrap() };
|
||||||
|
let mut commands=vec![Command::CreateElement(element.clone())];
|
||||||
|
if step==1 {
|
||||||
|
commands.push(Command::CreateRelation(Relation { id:k.allocation, alias:p.fresh_alias("Performs","relation",k.allocation), name:"Performs".into(), description:String::new(), body:RelationBody::Allocation { source:id(10),target:key } }));
|
||||||
|
} else {
|
||||||
|
for original in p.relations.values().filter(|r| [id(20),id(22),id(23)].contains(&r.id)) {
|
||||||
|
let mut r=original.clone();
|
||||||
|
match &mut r.body { RelationBody::Allocation{target,..}|RelationBody::RoleAssignment{target,..}=>*target=key,_=>unreachable!() }
|
||||||
|
commands.push(Command::EditRelation(r.id,r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if step==0 {
|
||||||
|
let mut node=p.diagrams[&id(2)].nodes[&id(41)].clone(); node.collapsed=false;
|
||||||
|
commands.push(Command::EditNode{diagram:id(2),original:node.id,node});
|
||||||
|
}
|
||||||
|
let mut with_element=p.clone(); with_element.elements.insert(key,element);
|
||||||
|
let (x,y)=[(108.,188.),(108.,298.),(132.,252.)][step];
|
||||||
|
commands.push(Command::CreateNode { diagram:id(2), node:NodePlacement { id:k.nodes[step], alias:with_element.fresh_alias(&format!("view_{}",with_element.named(key)),"node",k.nodes[step]), element:key,position:Position{x,y},size:Size{width:224.,height:94.},collapsed:false,port_anchors:vec![] } });
|
||||||
|
if step==2 {
|
||||||
|
// Grandchild expands first child to bottom370; sibling follows at394
|
||||||
|
// (24px reflow gap), independently predicted for this fixed fixture.
|
||||||
|
let mut sibling=p.diagrams[&id(2)].nodes[&k.nodes[1]].clone(); sibling.position.y=394.;
|
||||||
|
commands.push(Command::EditNode{diagram:id(2),original:sibling.id,node:sibling});
|
||||||
|
}
|
||||||
|
advance(&mut base,commands);
|
||||||
|
}
|
||||||
|
base
|
||||||
|
}
|
||||||
|
fn only(ids:Vec<Id>,role:&str)->Id { assert_eq!(ids.len(),1,"Exactly one {role}"); ids[0] }
|
||||||
|
fn admit(actual:&Project,layer:Layer,stage:usize)->Keys {
|
||||||
|
let mut k=Keys{children:[Id::nil();3],nodes:[Id::nil();3],allocation:Id::nil()};
|
||||||
|
if stage==0 { return k; }
|
||||||
|
let target=actual.relations[&id(20)].target();
|
||||||
|
k.children[0]=if stage==3 { actual.elements[&target].parent.unwrap() } else { target };
|
||||||
|
if stage>=2 {
|
||||||
|
k.children[1]=only(actual.elements.values().filter(|e|e.parent==Some(id(11))&&e.id!=k.children[0]&&e.kind()==work(layer)).map(|e|e.id).collect(),"second child");
|
||||||
|
k.allocation=only(actual.relations.values().filter(|r|r.kind()==RelationKind::Allocation&&r.source()==id(10)&&r.target()==k.children[1]).map(|r|r.id).collect(),"new sibling allocation");
|
||||||
|
}
|
||||||
|
if stage==3 { k.children[2]=target; }
|
||||||
|
for step in 0..stage {
|
||||||
|
let e=&actual.elements[&k.children[step]];
|
||||||
|
assert_eq!(e.kind(),work(layer)); assert_eq!(e.layer(),layer);
|
||||||
|
assert_eq!(e.parent,Some(if step==2{k.children[0]}else{id(11)}));
|
||||||
|
k.nodes[step]=only(actual.diagrams[&id(2)].nodes.values().filter(|n|n.element==e.id).map(|n|n.id).collect(),"child placement");
|
||||||
|
}
|
||||||
|
let seed=fixture(layer); let mut used=BTreeSet::new();
|
||||||
|
for key in k.children[..stage].iter().chain(&k.nodes[..stage]).copied().chain((stage>=2).then_some(k.allocation)) {
|
||||||
|
assert_eq!(key.get_version_num(),4,"Native fresh UUIDv4 required");
|
||||||
|
assert!(!seed.contains(key)&&used.insert(key),"Fresh distinct role identity required");
|
||||||
|
}
|
||||||
|
k
|
||||||
|
}
|
||||||
|
fn check(actual:&CompiledSourceProject,layer:Layer,stage:usize) {
|
||||||
|
let prediction=expected(layer,stage,admit(&actual.decoded.project,layer,stage));
|
||||||
|
assert_eq!(actual.decoded.project,prediction.decoded.project,"Complete typed model mismatch, including placements and references");
|
||||||
|
assert_eq!(actual.bundle,prediction.bundle,"Exact source mismatch, including included note bytes");
|
||||||
|
}
|
||||||
|
fn history(actual:&CompiledSourceProject,layer:Layer,stage:usize,first:&CompiledSourceProject,second:Option<&CompiledSourceProject>) {
|
||||||
|
check(first,layer,1);
|
||||||
|
let first_keys=admit(&first.decoded.project,layer,1);
|
||||||
|
let current_keys=admit(&actual.decoded.project,layer,stage);
|
||||||
|
assert_eq!(current_keys.children[0],first_keys.children[0],"First child identity must match original first-commit carrier");
|
||||||
|
assert_eq!(current_keys.nodes[0],first_keys.nodes[0],"First placement identity must match original first-commit carrier");
|
||||||
|
if stage==3 {
|
||||||
|
let second=second.expect("Original second-commit carrier required");
|
||||||
|
check(second,layer,2);
|
||||||
|
history(second,layer,2,first,None);
|
||||||
|
let second_keys=admit(&second.decoded.project,layer,2);
|
||||||
|
assert_eq!(current_keys.children[1],second_keys.children[1],"Sibling identity must persist");
|
||||||
|
assert_eq!(current_keys.nodes[1],second_keys.nodes[1],"Sibling placement identity must persist");
|
||||||
|
assert_eq!(current_keys.allocation,second_keys.allocation,"Sibling allocation identity must persist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn write_bundle(dir:&Path,raw:&SourceBundle) {
|
||||||
|
assert!(!dir.exists(),"Use a new evidence directory"); fs::create_dir_all(dir).unwrap();
|
||||||
|
for (name,bytes) in &raw.files { let path=dir.join(name); if let Some(parent)=path.parent(){fs::create_dir_all(parent).unwrap();} fs::write(path,bytes).unwrap(); }
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(SCHEMA_VERSION,7);
|
||||||
|
let args:Vec<_>=std::env::args().collect();
|
||||||
|
match args.get(1).map(String::as_str) {
|
||||||
|
Some("generate")=>{assert_eq!(args.len(),3);let dir=Path::new(&args[2]);assert!(!dir.exists()); for (name,layer) in [("oa",Layer::Oa),("sa",Layer::Sa)] {write_bundle(&dir.join(name),&bundle(&fixture(layer)).bundle);} println!("PASS deterministic OA/SA native fixtures generated");}
|
||||||
|
Some("check")=>{
|
||||||
|
let layer=match args[2].as_str(){"oa"=>Layer::Oa,"sa"=>Layer::Sa,_=>panic!("oa|sa")};
|
||||||
|
let stage=args[3].parse::<usize>().unwrap();assert!(stage<=3);
|
||||||
|
assert_eq!(args.len(),5+stage.saturating_sub(1),"Stages2/3 require original stage1/stage2 checkpoint directories");
|
||||||
|
let actual=load(Path::new(&args[4]));check(&actual,layer,stage);
|
||||||
|
if stage>=2 {
|
||||||
|
let first=load(&Path::new(&args[5]).join("project.arcadia"));
|
||||||
|
let second=if stage==3 {Some(load(&Path::new(&args[6]).join("project.arcadia")))} else {None};
|
||||||
|
history(&actual,layer,stage,&first,second.as_ref());
|
||||||
|
}
|
||||||
|
println!("PASS complete model, exact source and pinned history stage {stage}");
|
||||||
|
}
|
||||||
|
Some("snapshot")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));write_bundle(Path::new(&args[3]),&actual.bundle);println!("PASS immutable source checkpoint");}
|
||||||
|
Some("compare")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));let checkpoint=load(&Path::new(&args[3]).join(&actual.bundle.entry));assert_eq!(actual.bundle,checkpoint.bundle,"Saved reachable source bytes changed");assert_eq!(actual.decoded.project,checkpoint.decoded.project);println!("PASS exact checkpoint bytes and model");}
|
||||||
|
Some("self-check")=>{for layer in [Layer::Oa,Layer::Sa]{let keys=Keys{children:std::array::from_fn(|_|Id::new_v4()),nodes:std::array::from_fn(|_|Id::new_v4()),allocation:Id::new_v4()};for stage in 0..=3 {let candidate=expected(layer,stage,keys);check(&candidate,layer,stage);if stage>=2 {history(&candidate,layer,stage,&expected(layer,1,keys),(stage==3).then(||expected(layer,2,keys)).as_ref());}let mut altered=candidate.decoded.project.clone();altered.elements.get_mut(&id(11)).unwrap().description="Unrequested mutation".into();assert_ne!(expected(layer,stage,admit(&altered,layer,stage)).decoded.project,altered);}}println!("PASS oracle fixed points and unrelated-mutation discriminator");}
|
||||||
|
_=>panic!("generate DIR | check oa|sa STAGE ENTRY [STAGE1DIR] [STAGE2DIR] | snapshot ENTRY DIR | compare ENTRY DIR | self-check"),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Contextual/lifecycle integration checkpoint evidence — 2026-09-11
|
||||||
|
|
||||||
|
Codex-ytr83r /root/wsl_startup_review. Closed archival readback only: **11 payloads, 137778 bytes**, frozen at 2026-09-11T17:38:52.1137417Z. Original names/extensions and file bytes are preserved under their paths relative to .local. manifest.json records the exact inventory, original/copy SHA256, sizes and original modification times; readback.json records an independent second read of every original and destination. No discrepancy was found.
|
||||||
|
|
||||||
|
The seven gate payloads preserve the server/CLI all-targets exit 0 at 17:28:56.5857591 UTC (68 passed across seven targets), the initial semantic-schema exit 1 caused by missing jsonschema, its task-local installation log, and the successful repeated semantic-schema exit 0 at 17:28:34.201 UTC (72 fixtures; all 204 component schemas well formed). The failed attempt remains explicit evidence. The schema status records identify source 4da92ab481a2f5f9fcd88f29c90fa4785ac17c3a; the server/CLI record retains its own actual command/timestamps without inventing an absent commit field.
|
||||||
|
|
||||||
|
The other four payloads are the two-file wide-after-passive-save checkpoint (project.arcadia and rationale.arcadia), its same-oracle log, and wide-cjk-sa-resumed-min-125-saved.jpg. Root reports recovering the intended Studio, clicking Save all, and completing the snapshot/oracle comparison; the archived log says all identities, fields, reachable source and physical bytes match. This is the **old 47332A6E… / production 29084050 typography-wide passive-save check**, not corrected header pixels or new combined-desktop acceptance. The earlier 12-screenshot pre-correction archive remains separate and untouched.
|
||||||
|
|
||||||
|
This worker performed no native/oracle/compiler/schema/install execution, source edit, Git/index operation or WSL action. The installation log records root's already-completed work; archiving it did not install anything. No executable, DLL, private profile, growing log or Linux input is included. Root owns attributes, staging, integrated interpretation and the authoritative [issue 5](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/5) record.
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "WWWWWWWWWWWWWWWWWW" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "界界界界界界界界界界界界界界界界界界" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS same: all identities, fields, reachable source and physical bytes
|
||||||
|
After Width: | Height: | Size: 103 KiB |
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"identity": "Codex-ytr83r /root/wsl_startup_review",
|
||||||
|
"scope": "Closed Windows evidence archival; no new execution",
|
||||||
|
"frozenUtc": "2026-09-11T17:38:52.1137417Z",
|
||||||
|
"sourceRoot": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio",
|
||||||
|
"payloadCount": 11,
|
||||||
|
"payloadBytes": 137778,
|
||||||
|
"checkpointInventory": [
|
||||||
|
{
|
||||||
|
"name": "project.arcadia",
|
||||||
|
"bytes": 20128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rationale.arcadia",
|
||||||
|
"bytes": 86
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-server-cli-all-targets-20260911.log",
|
||||||
|
"destination": "ytr83r-server-cli-all-targets-20260911.log",
|
||||||
|
"bytes": 8885,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:28:55.8027952Z",
|
||||||
|
"sourceSha256": "91DD56837B62BE7AD95249B12A98D2C6C55FD62C91F1274FB90D616A76B4AE2E",
|
||||||
|
"destinationBytes": 8885,
|
||||||
|
"destinationSha256": "91DD56837B62BE7AD95249B12A98D2C6C55FD62C91F1274FB90D616A76B4AE2E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-server-cli-all-targets-20260911-status.json",
|
||||||
|
"destination": "ytr83r-server-cli-all-targets-20260911-status.json",
|
||||||
|
"bytes": 473,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:28:57.0914904Z",
|
||||||
|
"sourceSha256": "F3A0AD25C8232E45DF35F0656ED14E754F78A4F53CE3D8CE8EB082EE23860531",
|
||||||
|
"destinationBytes": 473,
|
||||||
|
"destinationSha256": "F3A0AD25C8232E45DF35F0656ED14E754F78A4F53CE3D8CE8EB082EE23860531"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-4da92ab.log",
|
||||||
|
"destination": "ytr83r-semantic-schema-4da92ab.log",
|
||||||
|
"bytes": 269,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:25:52.2283842Z",
|
||||||
|
"sourceSha256": "238A17DDBBC0EDBA0A3AA264F19DA3DA735A56B2DB6694B9F723A0F299C04FB8",
|
||||||
|
"destinationBytes": 269,
|
||||||
|
"destinationSha256": "238A17DDBBC0EDBA0A3AA264F19DA3DA735A56B2DB6694B9F723A0F299C04FB8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-4da92ab-status.json",
|
||||||
|
"destination": "ytr83r-semantic-schema-4da92ab-status.json",
|
||||||
|
"bytes": 279,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:25:52.31095Z",
|
||||||
|
"sourceSha256": "96D50DAADC4704C0A397F7B9803CB488B20BEA184D2D2CF4BEF55828B7935648",
|
||||||
|
"destinationBytes": 279,
|
||||||
|
"destinationSha256": "96D50DAADC4704C0A397F7B9803CB488B20BEA184D2D2CF4BEF55828B7935648"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-jsonschema-install.log",
|
||||||
|
"destination": "ytr83r-jsonschema-install.log",
|
||||||
|
"bytes": 1527,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:27:55.5204549Z",
|
||||||
|
"sourceSha256": "B7DE62BEB8A8AC0DF35516E3914DD57A12ACF556E61CA8E8E6E0CAC3728F4587",
|
||||||
|
"destinationBytes": 1527,
|
||||||
|
"destinationSha256": "B7DE62BEB8A8AC0DF35516E3914DD57A12ACF556E61CA8E8E6E0CAC3728F4587"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-localdeps-4da92ab.log",
|
||||||
|
"destination": "ytr83r-semantic-schema-localdeps-4da92ab.log",
|
||||||
|
"bytes": 136,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:28:34.1584199Z",
|
||||||
|
"sourceSha256": "EA459613E299F8686C8D9757288DFB32048D51CE0850A2B2161CFB048F09EAE8",
|
||||||
|
"destinationBytes": 136,
|
||||||
|
"destinationSha256": "EA459613E299F8686C8D9757288DFB32048D51CE0850A2B2161CFB048F09EAE8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-localdeps-4da92ab-status.json",
|
||||||
|
"destination": "ytr83r-semantic-schema-localdeps-4da92ab-status.json",
|
||||||
|
"bytes": 458,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:28:34.2213594Z",
|
||||||
|
"sourceSha256": "D0B4930C412279BE7F06F1F35F3A11D1BFA08DC1FB726ED71E8AD6DE28343A74",
|
||||||
|
"destinationBytes": 458,
|
||||||
|
"destinationSha256": "D0B4930C412279BE7F06F1F35F3A11D1BFA08DC1FB726ED71E8AD6DE28343A74"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/checkpoints/wide-after-passive-save/project.arcadia",
|
||||||
|
"destination": "lifecycle-native-ui/checkpoints/wide-after-passive-save/project.arcadia",
|
||||||
|
"bytes": 20128,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:34:01.5548598Z",
|
||||||
|
"sourceSha256": "6A18EE1F246321BDF8AB0E7E8E5D4EBA134CE486ED5224C4FFD61C7342C3A7D3",
|
||||||
|
"destinationBytes": 20128,
|
||||||
|
"destinationSha256": "6A18EE1F246321BDF8AB0E7E8E5D4EBA134CE486ED5224C4FFD61C7342C3A7D3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/checkpoints/wide-after-passive-save/rationale.arcadia",
|
||||||
|
"destination": "lifecycle-native-ui/checkpoints/wide-after-passive-save/rationale.arcadia",
|
||||||
|
"bytes": 86,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:34:01.5548598Z",
|
||||||
|
"sourceSha256": "8509F8D8A136D808CF98A1BB0FE50348B8A24409F30DEE4EE65B62BF5CB9E6BF",
|
||||||
|
"destinationBytes": 86,
|
||||||
|
"destinationSha256": "8509F8D8A136D808CF98A1BB0FE50348B8A24409F30DEE4EE65B62BF5CB9E6BF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/logs/wide-after-passive-save-same.log",
|
||||||
|
"destination": "lifecycle-native-ui/logs/wide-after-passive-save-same.log",
|
||||||
|
"bytes": 72,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:34:01.8606723Z",
|
||||||
|
"sourceSha256": "4B2B8F76684C906EDEDB026A1F661082ABF71033B50F8584EFF5C604D9882DED",
|
||||||
|
"destinationBytes": 72,
|
||||||
|
"destinationSha256": "4B2B8F76684C906EDEDB026A1F661082ABF71033B50F8584EFF5C604D9882DED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-resumed-min-125-saved.jpg",
|
||||||
|
"destination": "lifecycle-native-ui/screenshots/wide-cjk-sa-resumed-min-125-saved.jpg",
|
||||||
|
"bytes": 105465,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T17:33:38.7318371Z",
|
||||||
|
"sourceSha256": "1187D88DC5220EA17D8D38B63214E106860DE529C652894C9F3A7770EC2DD900",
|
||||||
|
"destinationBytes": 105465,
|
||||||
|
"destinationSha256": "1187D88DC5220EA17D8D38B63214E106860DE529C652894C9F3A7770EC2DD900"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"readbackUtc": "2026-09-11T17:38:52.5047939Z",
|
||||||
|
"payloadCount": 11,
|
||||||
|
"payloadBytes": 137778,
|
||||||
|
"allMatch": true,
|
||||||
|
"discrepancies": [],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-server-cli-all-targets-20260911.log",
|
||||||
|
"destination": "ytr83r-server-cli-all-targets-20260911.log",
|
||||||
|
"sourceBytes": 8885,
|
||||||
|
"destinationBytes": 8885,
|
||||||
|
"sourceSha256": "91DD56837B62BE7AD95249B12A98D2C6C55FD62C91F1274FB90D616A76B4AE2E",
|
||||||
|
"destinationSha256": "91DD56837B62BE7AD95249B12A98D2C6C55FD62C91F1274FB90D616A76B4AE2E",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-server-cli-all-targets-20260911-status.json",
|
||||||
|
"destination": "ytr83r-server-cli-all-targets-20260911-status.json",
|
||||||
|
"sourceBytes": 473,
|
||||||
|
"destinationBytes": 473,
|
||||||
|
"sourceSha256": "F3A0AD25C8232E45DF35F0656ED14E754F78A4F53CE3D8CE8EB082EE23860531",
|
||||||
|
"destinationSha256": "F3A0AD25C8232E45DF35F0656ED14E754F78A4F53CE3D8CE8EB082EE23860531",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-4da92ab.log",
|
||||||
|
"destination": "ytr83r-semantic-schema-4da92ab.log",
|
||||||
|
"sourceBytes": 269,
|
||||||
|
"destinationBytes": 269,
|
||||||
|
"sourceSha256": "238A17DDBBC0EDBA0A3AA264F19DA3DA735A56B2DB6694B9F723A0F299C04FB8",
|
||||||
|
"destinationSha256": "238A17DDBBC0EDBA0A3AA264F19DA3DA735A56B2DB6694B9F723A0F299C04FB8",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-4da92ab-status.json",
|
||||||
|
"destination": "ytr83r-semantic-schema-4da92ab-status.json",
|
||||||
|
"sourceBytes": 279,
|
||||||
|
"destinationBytes": 279,
|
||||||
|
"sourceSha256": "96D50DAADC4704C0A397F7B9803CB488B20BEA184D2D2CF4BEF55828B7935648",
|
||||||
|
"destinationSha256": "96D50DAADC4704C0A397F7B9803CB488B20BEA184D2D2CF4BEF55828B7935648",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-jsonschema-install.log",
|
||||||
|
"destination": "ytr83r-jsonschema-install.log",
|
||||||
|
"sourceBytes": 1527,
|
||||||
|
"destinationBytes": 1527,
|
||||||
|
"sourceSha256": "B7DE62BEB8A8AC0DF35516E3914DD57A12ACF556E61CA8E8E6E0CAC3728F4587",
|
||||||
|
"destinationSha256": "B7DE62BEB8A8AC0DF35516E3914DD57A12ACF556E61CA8E8E6E0CAC3728F4587",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-localdeps-4da92ab.log",
|
||||||
|
"destination": "ytr83r-semantic-schema-localdeps-4da92ab.log",
|
||||||
|
"sourceBytes": 136,
|
||||||
|
"destinationBytes": 136,
|
||||||
|
"sourceSha256": "EA459613E299F8686C8D9757288DFB32048D51CE0850A2B2161CFB048F09EAE8",
|
||||||
|
"destinationSha256": "EA459613E299F8686C8D9757288DFB32048D51CE0850A2B2161CFB048F09EAE8",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/ytr83r-semantic-schema-localdeps-4da92ab-status.json",
|
||||||
|
"destination": "ytr83r-semantic-schema-localdeps-4da92ab-status.json",
|
||||||
|
"sourceBytes": 458,
|
||||||
|
"destinationBytes": 458,
|
||||||
|
"sourceSha256": "D0B4930C412279BE7F06F1F35F3A11D1BFA08DC1FB726ED71E8AD6DE28343A74",
|
||||||
|
"destinationSha256": "D0B4930C412279BE7F06F1F35F3A11D1BFA08DC1FB726ED71E8AD6DE28343A74",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/checkpoints/wide-after-passive-save/project.arcadia",
|
||||||
|
"destination": "lifecycle-native-ui/checkpoints/wide-after-passive-save/project.arcadia",
|
||||||
|
"sourceBytes": 20128,
|
||||||
|
"destinationBytes": 20128,
|
||||||
|
"sourceSha256": "6A18EE1F246321BDF8AB0E7E8E5D4EBA134CE486ED5224C4FFD61C7342C3A7D3",
|
||||||
|
"destinationSha256": "6A18EE1F246321BDF8AB0E7E8E5D4EBA134CE486ED5224C4FFD61C7342C3A7D3",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/checkpoints/wide-after-passive-save/rationale.arcadia",
|
||||||
|
"destination": "lifecycle-native-ui/checkpoints/wide-after-passive-save/rationale.arcadia",
|
||||||
|
"sourceBytes": 86,
|
||||||
|
"destinationBytes": 86,
|
||||||
|
"sourceSha256": "8509F8D8A136D808CF98A1BB0FE50348B8A24409F30DEE4EE65B62BF5CB9E6BF",
|
||||||
|
"destinationSha256": "8509F8D8A136D808CF98A1BB0FE50348B8A24409F30DEE4EE65B62BF5CB9E6BF",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/logs/wide-after-passive-save-same.log",
|
||||||
|
"destination": "lifecycle-native-ui/logs/wide-after-passive-save-same.log",
|
||||||
|
"sourceBytes": 72,
|
||||||
|
"destinationBytes": 72,
|
||||||
|
"sourceSha256": "4B2B8F76684C906EDEDB026A1F661082ABF71033B50F8584EFF5C604D9882DED",
|
||||||
|
"destinationSha256": "4B2B8F76684C906EDEDB026A1F661082ABF71033B50F8584EFF5C604D9882DED",
|
||||||
|
"matchesFrozen": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-resumed-min-125-saved.jpg",
|
||||||
|
"destination": "lifecycle-native-ui/screenshots/wide-cjk-sa-resumed-min-125-saved.jpg",
|
||||||
|
"sourceBytes": 105465,
|
||||||
|
"destinationBytes": 105465,
|
||||||
|
"sourceSha256": "1187D88DC5220EA17D8D38B63214E106860DE529C652894C9F3A7770EC2DD900",
|
||||||
|
"destinationSha256": "1187D88DC5220EA17D8D38B63214E106860DE529C652894C9F3A7770EC2DD900",
|
||||||
|
"matchesFrozen": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"bytes": 2186,
|
||||||
|
"sha256": "F8C96D6CD1C7E796CAA8DF568BB6E1A6CFDEED57BC18F3A81E5BC2955493A0A5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "manifest.json",
|
||||||
|
"bytes": 5694,
|
||||||
|
"sha256": "D49E618F194868C3C3ABC2893893A8D52F6AD83460DBE6E0F2C94351AFC5F02C"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Collecting jsonschema
|
||||||
|
Downloading jsonschema-4.26.0-py3-none-any.whl.metadata (7.6 kB)
|
||||||
|
Collecting attrs>=22.2.0 (from jsonschema)
|
||||||
|
Downloading attrs-26.1.0-py3-none-any.whl.metadata (8.8 kB)
|
||||||
|
Collecting jsonschema-specifications>=2023.03.6 (from jsonschema)
|
||||||
|
Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl.metadata (2.9 kB)
|
||||||
|
Collecting referencing>=0.28.4 (from jsonschema)
|
||||||
|
Downloading referencing-0.37.0-py3-none-any.whl.metadata (2.8 kB)
|
||||||
|
Collecting rpds-py>=0.25.0 (from jsonschema)
|
||||||
|
Downloading rpds_py-2026.6.3-cp312-cp312-win_amd64.whl.metadata (4.2 kB)
|
||||||
|
Collecting typing-extensions>=4.4.0 (from referencing>=0.28.4->jsonschema)
|
||||||
|
Downloading typing_extensions-4.16.0-py3-none-any.whl.metadata (3.3 kB)
|
||||||
|
Downloading jsonschema-4.26.0-py3-none-any.whl (90 kB)
|
||||||
|
Downloading attrs-26.1.0-py3-none-any.whl (67 kB)
|
||||||
|
Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
|
||||||
|
Downloading referencing-0.37.0-py3-none-any.whl (26 kB)
|
||||||
|
Downloading rpds_py-2026.6.3-cp312-cp312-win_amd64.whl (220 kB)
|
||||||
|
Downloading typing_extensions-4.16.0-py3-none-any.whl (45 kB)
|
||||||
|
Installing collected packages: typing-extensions, rpds-py, attrs, referencing, jsonschema-specifications, jsonschema
|
||||||
|
|
||||||
|
Successfully installed attrs-26.1.0 jsonschema-4.26.0 jsonschema-specifications-2025.9.1 referencing-0.37.0 rpds-py-2026.6.3 typing-extensions-4.16.0
|
||||||
|
WARNING: Target directory C:\Users\User\Documents\Git\ARCADIA-Studio\.local\python-deps\bin already exists. Specify --upgrade to force replacement.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"started_utc": "2026-09-11T17:25:50.971Z",
|
||||||
|
"completed_utc": "2026-09-11T17:25:52.228Z",
|
||||||
|
"exit": 1,
|
||||||
|
"commit": "4da92ab481a2f5f9fcd88f29c90fa4785ac17c3a",
|
||||||
|
"command": [
|
||||||
|
"python",
|
||||||
|
"crates/oa-server/tests/semantic_schema.py",
|
||||||
|
"--check",
|
||||||
|
"--validate"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Traceback (most recent call last):
|
||||||
|
File "C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-server\tests\semantic_schema.py", line 266, in <module>
|
||||||
|
from jsonschema import Draft202012Validator, FormatChecker
|
||||||
|
ModuleNotFoundError: No module named 'jsonschema'
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"started_utc": "2026-09-11T17:28:23.175Z",
|
||||||
|
"completed_utc": "2026-09-11T17:28:34.201Z",
|
||||||
|
"exit": 0,
|
||||||
|
"commit": "4da92ab481a2f5f9fcd88f29c90fa4785ac17c3a",
|
||||||
|
"command": [
|
||||||
|
"python",
|
||||||
|
"-c",
|
||||||
|
"import sys, runpy; sys.path.insert(0, r'C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\python-deps'); sys.argv = ['crates/oa-server/tests/semantic_schema.py', '--check', '--validate']; runpy.run_path(sys.argv[0], run_name='__main__')"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Source-7 schema fixtures: 72 passed; all 204 component schemas well-formed
|
||||||
|
Closed catalog: 14 families, 90 scalar leaves; 204 schemas
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"exit": 0,
|
||||||
|
"command": [
|
||||||
|
"cargo",
|
||||||
|
"test",
|
||||||
|
"--locked",
|
||||||
|
"--offline",
|
||||||
|
"-p",
|
||||||
|
"oa-server",
|
||||||
|
"-p",
|
||||||
|
"oa-cli",
|
||||||
|
"--all-targets"
|
||||||
|
],
|
||||||
|
"log": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\ytr83r-server-cli-all-targets-20260911.log",
|
||||||
|
"completed_utc": "2026-09-11T17:28:56.5857591Z",
|
||||||
|
"name": "ytr83r-server-cli-all-targets-20260911",
|
||||||
|
"seconds": 1137.23368,
|
||||||
|
"started_utc": "2026-09-11T17:09:58.7368301Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
Compiling syn v2.0.119
|
||||||
|
Compiling synstructure v0.13.2
|
||||||
|
Compiling zerofrom-derive v0.1.7
|
||||||
|
Compiling zerofrom v0.1.8
|
||||||
|
Compiling yoke-derive v0.8.2
|
||||||
|
Compiling yoke v0.8.3
|
||||||
|
Compiling zerovec v0.11.8
|
||||||
|
Compiling smallvec v1.16.0
|
||||||
|
Compiling tinystr v0.8.4
|
||||||
|
Compiling icu_locale_core v2.3.0
|
||||||
|
Compiling zerotrie v0.2.5
|
||||||
|
Compiling potential_utf v0.1.6
|
||||||
|
Compiling icu_collections v2.3.0
|
||||||
|
Compiling icu_provider v2.3.1
|
||||||
|
Compiling futures-task v0.3.34
|
||||||
|
Compiling slab v0.4.12
|
||||||
|
Compiling futures-util v0.3.34
|
||||||
|
Compiling icu_normalizer v2.3.0
|
||||||
|
Compiling icu_properties v2.3.0
|
||||||
|
Compiling futures-channel v0.3.34
|
||||||
|
Compiling hyper v1.11.1
|
||||||
|
Compiling tracing v0.1.44
|
||||||
|
Compiling idna_adapter v1.2.2
|
||||||
|
Compiling hyper-util v0.1.20
|
||||||
|
Compiling idna v1.1.0
|
||||||
|
Compiling tower v0.5.3
|
||||||
|
Compiling url v2.5.8
|
||||||
|
Compiling tower-http v0.6.11
|
||||||
|
Compiling axum v0.8.9
|
||||||
|
Compiling hyper-rustls v0.27.9
|
||||||
|
Compiling reqwest v0.12.28
|
||||||
|
Compiling oa-server v0.2.0 (C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-server)
|
||||||
|
Compiling oa-cli v0.2.0 (C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-cli)
|
||||||
|
Finished `test` profile [unoptimized] target(s) in 18m 12s
|
||||||
|
Running unittests src\lib.rs (target\debug\deps\oa_cli-2381a185bffed18a.exe)
|
||||||
|
|
||||||
|
running 0 tests
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||||
|
|
||||||
|
Running unittests src\main.rs (target\debug\deps\arcadia-9f7631654f61a956.exe)
|
||||||
|
|
||||||
|
running 0 tests
|
||||||
|
Running tests\source_cli.rs (target\debug\deps\source_cli-e1d35ae8856e0c2f.exe)
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||||
|
|
||||||
|
|
||||||
|
running 12 tests
|
||||||
|
test captured_candidate_exact_write_preserves_crlf_comments_and_noop_metadata ... ok
|
||||||
|
test alias_rename_across_modules_changes_only_live_tokens_and_retains_all_identity ... ok
|
||||||
|
test captured_dependency_change_refuses_all_writes_even_on_noop ... ok
|
||||||
|
test entry_is_explicit_and_machine_readable_usage_errors_do_not_write ... ok
|
||||||
|
test invalid_unicode_raw_edit_is_located_and_never_stabilized_or_written ... ok
|
||||||
|
test complete_alarm_example_preview_then_write_and_fixed_point ... ok
|
||||||
|
test missing_id_warning_uses_raw_location_and_never_exposes_ephemeral_lint_id ... ok
|
||||||
|
test loader_encoding_missing_dependency_and_invalid_old_source_keep_original_bytes ... ok
|
||||||
|
test non_element_alias_detached_file_and_unchanged_dependency_conflict ... ok
|
||||||
|
test pending_journal_refuses_read_preview_and_write_without_recovery ... ok
|
||||||
|
test rename_shows_missing_identity_and_separate_source_migration ... ok
|
||||||
|
test rejected_alias_and_same_alias_preserve_source ... ok
|
||||||
|
|
||||||
|
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.58s
|
||||||
|
|
||||||
|
Running unittests src\lib.rs (target\debug\deps\oa_server-ef5ecc53eb3cd700.exe)
|
||||||
|
|
||||||
|
running 4 tests
|
||||||
|
test store::preparation_gate::preparation_gate_drop_cancels_without_installing_candidate ... ok
|
||||||
|
test store::tests::prepared_candidates_recheck_revision_at_commit_and_failed_loser_is_not_installed ... ok
|
||||||
|
test preparation_http_tests::prepared_source_http_rechecks_revision_without_holding_catalog ... ok
|
||||||
|
test store::tests::source_bytes_are_charged_on_legacy_open_update_and_delete ... ok
|
||||||
|
|
||||||
|
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.72s
|
||||||
|
|
||||||
|
Running unittests src\main.rs (target\debug\deps\arcadia_server-4822c314ca80ed7f.exe)
|
||||||
|
|
||||||
|
running 0 tests
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||||
|
|
||||||
|
Running tests\http.rs (target\debug\deps\http-bb2e0bfd26dc3c6b.exe)
|
||||||
|
|
||||||
|
running 50 tests
|
||||||
|
test auth_origins_media_and_body_limits_are_enforced_over_http ... ok
|
||||||
|
test batches_dry_run_noop_and_source_share_the_core ... ok
|
||||||
|
test chunked_upload_limits_and_loopback_host_check_use_the_real_transport ... ok
|
||||||
|
test communication_limits::ordinary_transition_operation_omission_limit_rejects_201_without_mutation ... ok
|
||||||
|
test communication_limits::ordinary_transition_endpoint_decision_limit_rejects_201_without_mutation ... ok
|
||||||
|
test communication_limits::semantic_transition_operation_omission_limit_rejects_201_without_mutation ... ok
|
||||||
|
test communication_limits::semantic_transition_endpoint_decision_limit_rejects_201_without_mutation ... ok
|
||||||
|
test conditional_writes_are_atomic_and_survive_restart ... ok
|
||||||
|
test data_contracts::authored_schema3_bundle_upgrade_reports_data_defaults_and_preserves_files ... ok
|
||||||
|
test data_contracts::existing_schema3_coupled_envelope_upgrades_in_memory_until_explicit_save ... ok
|
||||||
|
test data_contracts::published_data_contract_queries_atomic_edits_identity_and_restart ... ok
|
||||||
|
test current_migration_reports_and_invalid_typed_transaction_preserve_http_state ... ok
|
||||||
|
test edge_route_transactions_source_and_json_share_defaults_etags_and_atomic_rejection ... ok
|
||||||
|
test data_contracts::qualified_call_reply_values_reject_invalid_edits_and_survive_identity_source_and_restart ... ok
|
||||||
|
test failed_atomic_replacement_preserves_the_accepted_model ... ok
|
||||||
|
test fixed_store_capacity_is_enforced_on_http_and_restart ... ok
|
||||||
|
test graph_filters_pagination_and_bounded_neighborhoods ... ok
|
||||||
|
test invalid_candidates_and_bad_headers_leave_snapshot_unchanged ... ok
|
||||||
|
test legacy_json_import_and_model_reidentification_keep_the_service_address ... ok
|
||||||
|
test mission_and_contextual_chain_records_round_trip_and_edit_through_http ... ok
|
||||||
|
test non_loopback_requires_a_token ... ok
|
||||||
|
test legacy_v2_scenarios_and_typed_message_edits_use_the_same_http_model ... ok
|
||||||
|
test port_anchors::port_anchor_http_commands_and_source_edits_preserve_per_view_state_after_restart ... ok
|
||||||
|
test port_anchors::port_anchor_http_rejects_invalid_records_and_stale_owners_atomically ... ok
|
||||||
|
test port_anchors::port_anchor_served_openapi_matches_serialized_records_and_commands ... ok
|
||||||
|
test reopening_v2_envelope_reports_migration_without_saving_or_advancing_revision ... ok
|
||||||
|
test route_openapi_describes_the_serde_command_shapes_and_nested_defaults ... ok
|
||||||
|
test semantic_transitions::semantic_candidate_constructor_reverse_direction_and_explicit_trace_selection ... ok
|
||||||
|
test semantic_transitions::semantic_cleanup_impacts_require_exact_acceptance_before_atomic_apply ... ok
|
||||||
|
test semantic_transitions::semantic_new_mapping_keeps_preview_generated_identifiers ... ok
|
||||||
|
test semantic_transitions::semantic_partial_review_query_source_and_restart_share_one_revision ... ok
|
||||||
|
test semantic_transitions::semantic_resets_and_invalid_candidates_preserve_deliberate_review_boundaries ... ok
|
||||||
|
test semantic_transitions::semantic_strict_rejection_stale_plans_and_source_only_changes_never_write ... ok
|
||||||
|
test source6_views::source6_http_explicit_communication_ports_preview_apply_and_restart_preserve_decisions ... ok
|
||||||
|
test source6_views::source6_http_views_categories_and_means_preserve_source_and_reject_invalid_batch_after_restart ... ok
|
||||||
|
test source7_contracts::source7_http_instance_and_operation_edits_preserve_identity_source_and_restart ... ok
|
||||||
|
test source_projects::source_projects_command_dry_runs_and_identity_swaps_preserve_lexical_ownership ... ok
|
||||||
|
test source_projects::source_projects_concurrent_comment_and_semantic_writes_share_one_revision ... ok
|
||||||
|
test source_projects::source_projects_equal_models_still_apply_changed_identity_provenance ... ok
|
||||||
|
test source_projects::source_projects_failed_atomic_comment_write_preserves_both_representations ... ok
|
||||||
|
test source_projects::source_projects_invalid_dependencies_and_duplicate_paths_never_change_the_head ... ok
|
||||||
|
test source_projects::source_projects_model_interchange_and_transitions_keep_authored_modules ... ok
|
||||||
|
test source_projects::source_projects_openapi_describes_the_coupled_envelope_and_source_diagnostics ... ok
|
||||||
|
test source_projects::source_projects_preserve_exact_files_comments_and_comment_only_revisions ... ok
|
||||||
|
test oversized_source_and_json_are_rejected_without_stalling_the_catalog ... ok
|
||||||
|
test source_projects::source_projects_upgrade_old_envelopes_without_writes_and_reject_inconsistent_pairs ... ok
|
||||||
|
test storage_rejects_second_owners_corruption_and_identity_mismatch ... ok
|
||||||
|
test transition_preview_apply_and_artifact_links_preserve_revision_boundaries ... ok
|
||||||
|
test typed_state_events_batch_reidentification_and_deletion_use_real_http ... ok
|
||||||
|
test source_projects::source_projects_stabilize_once_and_enforce_size_after_insertions ... ok
|
||||||
|
|
||||||
|
test result: ok. 50 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 34.63s
|
||||||
|
|
||||||
|
Running tests\state_representation.rs (target\debug\deps\state_representation-6b7303f63f64aeaf.exe)
|
||||||
|
|
||||||
|
running 2 tests
|
||||||
|
test served_state_projection_schema_preserves_closed_string_and_payload_variants ... ok
|
||||||
|
test state_view_source_json_transactions_and_restart_share_the_published_contract ... ok
|
||||||
|
|
||||||
|
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.94s
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 193 KiB |
@@ -0,0 +1,156 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-create-selected-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-create-selected-wide-100.jpg",
|
||||||
|
"sourceBytes": 175454,
|
||||||
|
"destinationBytes": 175454,
|
||||||
|
"sourceSha256": "2933CE4478B01AD9A7ACF54328A2A1996654603D486ACCBA14775EC31EF4BFE9",
|
||||||
|
"sourceReadbackSha256": "2933CE4478B01AD9A7ACF54328A2A1996654603D486ACCBA14775EC31EF4BFE9",
|
||||||
|
"destinationSha256": "2933CE4478B01AD9A7ACF54328A2A1996654603D486ACCBA14775EC31EF4BFE9",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-header-selected-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-header-selected-wide-100.jpg",
|
||||||
|
"sourceBytes": 164799,
|
||||||
|
"destinationBytes": 164799,
|
||||||
|
"sourceSha256": "47D88ABAFA3AD8F6C9AFBDE4B1715305D5224676EBF1C1E96ADEA28CFA2FBE12",
|
||||||
|
"sourceReadbackSha256": "47D88ABAFA3AD8F6C9AFBDE4B1715305D5224676EBF1C1E96ADEA28CFA2FBE12",
|
||||||
|
"destinationSha256": "47D88ABAFA3AD8F6C9AFBDE4B1715305D5224676EBF1C1E96ADEA28CFA2FBE12",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-wide-100.jpg",
|
||||||
|
"sourceBytes": 146098,
|
||||||
|
"destinationBytes": 146098,
|
||||||
|
"sourceSha256": "01B50A30ABE4EEADAF650466689F8CC5BFF863D38847FA0187FAFE2B068D42B6",
|
||||||
|
"sourceReadbackSha256": "01B50A30ABE4EEADAF650466689F8CC5BFF863D38847FA0187FAFE2B068D42B6",
|
||||||
|
"destinationSha256": "01B50A30ABE4EEADAF650466689F8CC5BFF863D38847FA0187FAFE2B068D42B6",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-100.jpg",
|
||||||
|
"sourceBytes": 107857,
|
||||||
|
"destinationBytes": 107857,
|
||||||
|
"sourceSha256": "3EB8E38F3265A8EFB88B05CCCB189A1F63FB0425B0FB2F84AF5AB5F01ABA5607",
|
||||||
|
"sourceReadbackSha256": "3EB8E38F3265A8EFB88B05CCCB189A1F63FB0425B0FB2F84AF5AB5F01ABA5607",
|
||||||
|
"destinationSha256": "3EB8E38F3265A8EFB88B05CCCB189A1F63FB0425B0FB2F84AF5AB5F01ABA5607",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-51.jpg",
|
||||||
|
"sourceBytes": 97530,
|
||||||
|
"destinationBytes": 97530,
|
||||||
|
"sourceSha256": "67E861B7275D01F1338B773A86041F6696A1B889007CEE4CAF7EB4ADD715508C",
|
||||||
|
"sourceReadbackSha256": "67E861B7275D01F1338B773A86041F6696A1B889007CEE4CAF7EB4ADD715508C",
|
||||||
|
"destinationSha256": "67E861B7275D01F1338B773A86041F6696A1B889007CEE4CAF7EB4ADD715508C",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-header-overflow-min-80.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-header-overflow-min-80.jpg",
|
||||||
|
"sourceBytes": 104939,
|
||||||
|
"destinationBytes": 104939,
|
||||||
|
"sourceSha256": "1F524379D4097F27413FA104D10C74CC21338779EB0D7A080119BB4A09AC31AD",
|
||||||
|
"sourceReadbackSha256": "1F524379D4097F27413FA104D10C74CC21338779EB0D7A080119BB4A09AC31AD",
|
||||||
|
"destinationSha256": "1F524379D4097F27413FA104D10C74CC21338779EB0D7A080119BB4A09AC31AD",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-header-selected-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-header-selected-min-51.jpg",
|
||||||
|
"sourceBytes": 97889,
|
||||||
|
"destinationBytes": 97889,
|
||||||
|
"sourceSha256": "021CE1ABAA830868BFE19FBACF8C834E9D7329FE7C7BAE4D419E9EC51E68BE22",
|
||||||
|
"sourceReadbackSha256": "021CE1ABAA830868BFE19FBACF8C834E9D7329FE7C7BAE4D419E9EC51E68BE22",
|
||||||
|
"destinationSha256": "021CE1ABAA830868BFE19FBACF8C834E9D7329FE7C7BAE4D419E9EC51E68BE22",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-100.jpg",
|
||||||
|
"sourceBytes": 103939,
|
||||||
|
"destinationBytes": 103939,
|
||||||
|
"sourceSha256": "B1592F3C8A33EB13654A94FDDC9B966A906D25ACA14C30018B53A8B33E2A78C6",
|
||||||
|
"sourceReadbackSha256": "B1592F3C8A33EB13654A94FDDC9B966A906D25ACA14C30018B53A8B33E2A78C6",
|
||||||
|
"destinationSha256": "B1592F3C8A33EB13654A94FDDC9B966A906D25ACA14C30018B53A8B33E2A78C6",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-51.jpg",
|
||||||
|
"sourceBytes": 97424,
|
||||||
|
"destinationBytes": 97424,
|
||||||
|
"sourceSha256": "E7F6DD6F727695F0D72948FA592B5FE1E1AFA106E111D9DD0764CD179E16A101",
|
||||||
|
"sourceReadbackSha256": "E7F6DD6F727695F0D72948FA592B5FE1E1AFA106E111D9DD0764CD179E16A101",
|
||||||
|
"destinationSha256": "E7F6DD6F727695F0D72948FA592B5FE1E1AFA106E111D9DD0764CD179E16A101",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-fit.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-fit.jpg",
|
||||||
|
"sourceBytes": 100534,
|
||||||
|
"destinationBytes": 100534,
|
||||||
|
"sourceSha256": "9689D10FF405CFD85CE4E69842C33859E9FC2A622A1096E7E1FF3F11CAA542AE",
|
||||||
|
"sourceReadbackSha256": "9689D10FF405CFD85CE4E69842C33859E9FC2A622A1096E7E1FF3F11CAA542AE",
|
||||||
|
"destinationSha256": "9689D10FF405CFD85CE4E69842C33859E9FC2A622A1096E7E1FF3F11CAA542AE",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-wide-100.jpg",
|
||||||
|
"sourceBytes": 175337,
|
||||||
|
"destinationBytes": 175337,
|
||||||
|
"sourceSha256": "C025CE484C8807C9D0B1A87534EE2B85D89088DC6EE69A5124B38C983FCB9DEE",
|
||||||
|
"sourceReadbackSha256": "C025CE484C8807C9D0B1A87534EE2B85D89088DC6EE69A5124B38C983FCB9DEE",
|
||||||
|
"destinationSha256": "C025CE484C8807C9D0B1A87534EE2B85D89088DC6EE69A5124B38C983FCB9DEE",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-wide-195.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-wide-195.jpg",
|
||||||
|
"sourceBytes": 198013,
|
||||||
|
"destinationBytes": 198013,
|
||||||
|
"sourceSha256": "F23F56F5FE040352FA6962F75075EFE225A626B747CEE98DEF876DAAFA221C88",
|
||||||
|
"sourceReadbackSha256": "F23F56F5FE040352FA6962F75075EFE225A626B747CEE98DEF876DAAFA221C88",
|
||||||
|
"destinationSha256": "F23F56F5FE040352FA6962F75075EFE225A626B747CEE98DEF876DAAFA221C88",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/ytr83r-scaled-workspace-test-let-chain-status.json",
|
||||||
|
"destinationRelativePath": "ytr83r-scaled-workspace-test-let-chain-status.json",
|
||||||
|
"sourceBytes": 550,
|
||||||
|
"destinationBytes": 550,
|
||||||
|
"sourceSha256": "DC06DCF0151936E5D8B28BA2962F2226D1301832194841961D1E4C16D0BDB6C7",
|
||||||
|
"sourceReadbackSha256": "DC06DCF0151936E5D8B28BA2962F2226D1301832194841961D1E4C16D0BDB6C7",
|
||||||
|
"destinationSha256": "DC06DCF0151936E5D8B28BA2962F2226D1301832194841961D1E4C16D0BDB6C7",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/ytr83r-scaled-workspace-test-let-chain.log",
|
||||||
|
"destinationRelativePath": "ytr83r-scaled-workspace-test-let-chain.log",
|
||||||
|
"sourceBytes": 507,
|
||||||
|
"destinationBytes": 507,
|
||||||
|
"sourceSha256": "17FC59C4A00C1B3E37E6EFAEA5377E47E32D1284CCF5B0DA69B8A649280A4F3E",
|
||||||
|
"sourceReadbackSha256": "17FC59C4A00C1B3E37E6EFAEA5377E47E32D1284CCF5B0DA69B8A649280A4F3E",
|
||||||
|
"destinationSha256": "17FC59C4A00C1B3E37E6EFAEA5377E47E32D1284CCF5B0DA69B8A649280A4F3E",
|
||||||
|
"sourceUnchanged": true,
|
||||||
|
"byteExact": true
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"copyCompletedUtc": "2026-09-11T17:14:21.6163102Z",
|
||||||
|
"secondReadbackUtc": "2026-09-11T17:14:22.0424380Z",
|
||||||
|
"payloadFileCount": 14,
|
||||||
|
"payloadBytes": 1570870.0,
|
||||||
|
"screenshotCount": 12,
|
||||||
|
"inputDiscrepancies": [],
|
||||||
|
"copyDiscrepancies": [],
|
||||||
|
"exactPayloadInventory": true,
|
||||||
|
"secondReadbackHashMatches": true,
|
||||||
|
"inventoryDifferences": [],
|
||||||
|
"rehashDifferences": [],
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"owner": "Codex-ytr83r /root/wsl_startup_review",
|
||||||
|
"frozenUtc": "2026-09-11T17:14:15.6513799Z",
|
||||||
|
"sourceWorkspace": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio",
|
||||||
|
"archivalOnly": true,
|
||||||
|
"nativeOrOracleExecuted": false,
|
||||||
|
"expectedScreenshotCount": 12,
|
||||||
|
"actualScreenshotCount": 12,
|
||||||
|
"inputDiscrepancies": [],
|
||||||
|
"fileCount": 14,
|
||||||
|
"totalSourceBytes": 1570870.0,
|
||||||
|
"screenshotNames": [
|
||||||
|
"wide-cjk-sa-create-selected-wide-100.jpg",
|
||||||
|
"wide-cjk-sa-header-selected-wide-100.jpg",
|
||||||
|
"wide-cjk-sa-wide-100.jpg",
|
||||||
|
"wide-latin-oa-create-selected-min-100.jpg",
|
||||||
|
"wide-latin-oa-create-selected-min-51.jpg",
|
||||||
|
"wide-latin-oa-header-overflow-min-80.jpg",
|
||||||
|
"wide-latin-oa-header-selected-min-51.jpg",
|
||||||
|
"wide-latin-oa-min-100.jpg",
|
||||||
|
"wide-latin-oa-min-51.jpg",
|
||||||
|
"wide-latin-oa-min-fit.jpg",
|
||||||
|
"wide-latin-oa-wide-100.jpg",
|
||||||
|
"wide-latin-oa-wide-195.jpg"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-create-selected-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-create-selected-wide-100.jpg",
|
||||||
|
"sourceBytes": 175454,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:58:00.3505572Z",
|
||||||
|
"sourceSha256": "2933CE4478B01AD9A7ACF54328A2A1996654603D486ACCBA14775EC31EF4BFE9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-header-selected-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-header-selected-wide-100.jpg",
|
||||||
|
"sourceBytes": 164799,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:58:14.7973744Z",
|
||||||
|
"sourceSha256": "47D88ABAFA3AD8F6C9AFBDE4B1715305D5224676EBF1C1E96ADEA28CFA2FBE12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-cjk-sa-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-cjk-sa-wide-100.jpg",
|
||||||
|
"sourceBytes": 146098,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:57:49.7849401Z",
|
||||||
|
"sourceSha256": "01B50A30ABE4EEADAF650466689F8CC5BFF863D38847FA0187FAFE2B068D42B6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-100.jpg",
|
||||||
|
"sourceBytes": 107857,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:53:11.7942396Z",
|
||||||
|
"sourceSha256": "3EB8E38F3265A8EFB88B05CCCB189A1F63FB0425B0FB2F84AF5AB5F01ABA5607"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-create-selected-min-51.jpg",
|
||||||
|
"sourceBytes": 97530,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:16.6520726Z",
|
||||||
|
"sourceSha256": "67E861B7275D01F1338B773A86041F6696A1B889007CEE4CAF7EB4ADD715508C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-header-overflow-min-80.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-header-overflow-min-80.jpg",
|
||||||
|
"sourceBytes": 104939,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:54:19.3300256Z",
|
||||||
|
"sourceSha256": "1F524379D4097F27413FA104D10C74CC21338779EB0D7A080119BB4A09AC31AD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-header-selected-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-header-selected-min-51.jpg",
|
||||||
|
"sourceBytes": 97889,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:04.9626536Z",
|
||||||
|
"sourceSha256": "021CE1ABAA830868BFE19FBACF8C834E9D7329FE7C7BAE4D419E9EC51E68BE22"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-100.jpg",
|
||||||
|
"sourceBytes": 103939,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:52:53.1157425Z",
|
||||||
|
"sourceSha256": "B1592F3C8A33EB13654A94FDDC9B966A906D25ACA14C30018B53A8B33E2A78C6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-51.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-51.jpg",
|
||||||
|
"sourceBytes": 97424,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:55:05.6164194Z",
|
||||||
|
"sourceSha256": "E7F6DD6F727695F0D72948FA592B5FE1E1AFA106E111D9DD0764CD179E16A101"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-min-fit.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-min-fit.jpg",
|
||||||
|
"sourceBytes": 100534,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:31.9395194Z",
|
||||||
|
"sourceSha256": "9689D10FF405CFD85CE4E69842C33859E9FC2A622A1096E7E1FF3F11CAA542AE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-wide-100.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-wide-100.jpg",
|
||||||
|
"sourceBytes": 175337,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:48.7745936Z",
|
||||||
|
"sourceSha256": "C025CE484C8807C9D0B1A87534EE2B85D89088DC6EE69A5124B38C983FCB9DEE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/lifecycle-native-ui/screenshots/wide-latin-oa-wide-195.jpg",
|
||||||
|
"destinationRelativePath": "lifecycle-native-ui/screenshots/wide-latin-oa-wide-195.jpg",
|
||||||
|
"sourceBytes": 198013,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:57:07.8785037Z",
|
||||||
|
"sourceSha256": "F23F56F5FE040352FA6962F75075EFE225A626B747CEE98DEF876DAAFA221C88"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/ytr83r-scaled-workspace-test-let-chain-status.json",
|
||||||
|
"destinationRelativePath": "ytr83r-scaled-workspace-test-let-chain-status.json",
|
||||||
|
"sourceBytes": 550,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:53.1891102Z",
|
||||||
|
"sourceSha256": "DC06DCF0151936E5D8B28BA2962F2226D1301832194841961D1E4C16D0BDB6C7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sourceRelativePath": ".local/ytr83r-scaled-workspace-test-let-chain.log",
|
||||||
|
"destinationRelativePath": "ytr83r-scaled-workspace-test-let-chain.log",
|
||||||
|
"sourceBytes": 507,
|
||||||
|
"sourceLastWriteUtc": "2026-09-11T16:56:52.6176710Z",
|
||||||
|
"sourceSha256": "17FC59C4A00C1B3E37E6EFAEA5377E47E32D1284CCF5B0DA69B8A649280A4F3E"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"completed_utc": "2026-09-11T16:56:53.0694635Z",
|
||||||
|
"name": "ytr83r-scaled-workspace-test-let-chain",
|
||||||
|
"seconds": 408.2847446,
|
||||||
|
"command": [
|
||||||
|
"cargo",
|
||||||
|
"test",
|
||||||
|
"--locked",
|
||||||
|
"--offline",
|
||||||
|
"-p",
|
||||||
|
"oa-desktop",
|
||||||
|
"--bin",
|
||||||
|
"arcadia-studio",
|
||||||
|
"scenario_workspace_scaled_and_scrolled_scene_keeps_clicked_slots_and_message_"
|
||||||
|
],
|
||||||
|
"log": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\ytr83r-scaled-workspace-test-let-chain.log",
|
||||||
|
"exit": 0,
|
||||||
|
"started_utc": "2026-09-11T16:50:04.6110664Z"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Compiling oa-desktop v0.2.0 (C:\Users\User\Documents\Git\ARCADIA-Studio\crates\oa-desktop)
|
||||||
|
Finished `test` profile [unoptimized] target(s) in 6m 46s
|
||||||
|
Running unittests src\main.rs (target\debug\deps\arcadia_studio-df984b90c0583184.exe)
|
||||||
|
|
||||||
|
running 1 test
|
||||||
|
test behavior::scenario::workspace::tests::scenario_workspace_scaled_and_scrolled_scene_keeps_clicked_slots_and_message_endpoints ... ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 674 filtered out; finished in 0.04s
|
||||||
|
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver · reviewed" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver · reviewed" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver · reviewed" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1,692 @@
|
|||||||
|
arcadia 7;
|
||||||
|
project lifecycle_native "Lifecycle native contract" id "5ceab400-0000-4000-8000-000000000001" {
|
||||||
|
include "rationale.arcadia";
|
||||||
|
description "Fixed semantic inputs; lifecycle geometry is derived and never authored.";
|
||||||
|
|
||||||
|
oa_activity life_oa_10 "OA Controller work" id "5ceab400-0000-4000-8000-0000000003f2" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_activity life_oa_11 "OA Receiver work" id "5ceab400-0000-4000-8000-0000000003f3" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_100 "OA 01 Root create action delete" id "5ceab400-0000-4000-8000-00000000044c" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_110, @life_oa_111, @life_oa_112];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_101 "OA Controller lane" id "5ceab400-0000-4000-8000-00000000044d" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_102 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000044e" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_110 "OA Create receiver" id "5ceab400-0000-4000-8000-000000000456" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_111 "OA Receiver action" id "5ceab400-0000-4000-8000-000000000457" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_oa_11;
|
||||||
|
lifeline @life_oa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_112 "OA Delete receiver" id "5ceab400-0000-4000-8000-000000000458" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_200 "OA 02 Preexisting empty" id "5ceab400-0000-4000-8000-0000000004b0" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_201 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000004b1" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_202 "OA Receiver lane" id "5ceab400-0000-4000-8000-0000000004b2" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_200;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_300 "OA 03 Create only" id "5ceab400-0000-4000-8000-000000000514" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_310];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_301 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000515" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_302 "OA Receiver lane" id "5ceab400-0000-4000-8000-000000000516" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_310 "OA Create only receiver" id "5ceab400-0000-4000-8000-00000000051e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_400 "OA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000578" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_410, @life_oa_411];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_401 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000579" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_402 "OA Receiver lane" id "5ceab400-0000-4000-8000-00000000057a" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
represented @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_410 "OA Open call" id "5ceab400-0000-4000-8000-000000000582" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_411 "OA ALT delete or keep" id "5ceab400-0000-4000-8000-000000000583" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_oa_412, @life_oa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_412 "OA Left delete operand" id "5ceab400-0000-4000-8000-000000000584" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_oa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_413 "OA Right empty operand" id "5ceab400-0000-4000-8000-000000000585" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_414 "OA Conditional delete receiver" id "5ceab400-0000-4000-8000-000000000586" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_oa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_500 "OA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000005dc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_510, @life_oa_511];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_501 "OA Controller lane" id "5ceab400-0000-4000-8000-0000000005dd" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_502 "OA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000005de" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_510 "OA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e6" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_511 "OA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000005e7" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_scenario life_oa_600 "OA 06 Self delete" id "5ceab400-0000-4000-8000-000000000640" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_oa_610];
|
||||||
|
viewpoint OperationalActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_lifeline life_oa_601 "OA Controller lane" id "5ceab400-0000-4000-8000-000000000641" {
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
represented @life_oa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
oa_occurrence life_oa_610 "OA Self delete controller" id "5ceab400-0000-4000-8000-00000000064a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_oa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_oa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_oa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_10 "SA Controller work" id "5ceab400-0000-4000-8000-0000000007da" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_function life_sa_11 "SA Receiver work" id "5ceab400-0000-4000-8000-0000000007db" {
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_100 "SA 01 Root create action delete" id "5ceab400-0000-4000-8000-000000000834" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_110, @life_sa_111, @life_sa_112];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_101 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000835" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_102 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000836" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_110 "SA Create receiver" id "5ceab400-0000-4000-8000-00000000083e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_111 "SA Receiver action" id "5ceab400-0000-4000-8000-00000000083f" {
|
||||||
|
body {
|
||||||
|
Action {
|
||||||
|
action @life_sa_11;
|
||||||
|
lifeline @life_sa_102;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_112 "SA Delete receiver" id "5ceab400-0000-4000-8000-000000000840" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_102;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_101;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_100;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_200 "SA 02 Preexisting empty" id "5ceab400-0000-4000-8000-000000000898" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_201 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000899" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_202 "SA Receiver lane" id "5ceab400-0000-4000-8000-00000000089a" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_200;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_300 "SA 03 Create only" id "5ceab400-0000-4000-8000-0000000008fc" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_310];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_301 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000008fd" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_302 "SA Receiver lane" id "5ceab400-0000-4000-8000-0000000008fe" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_310 "SA Create only receiver" id "5ceab400-0000-4000-8000-000000000906" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_302;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_301;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_300;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_400 "SA 04 Conditional delete" id "5ceab400-0000-4000-8000-000000000960" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_410, @life_sa_411];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_401 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000961" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_402 "SA Receiver lane" id "5ceab400-0000-4000-8000-000000000962" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
represented @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_410 "SA Open call" id "5ceab400-0000-4000-8000-00000000096a" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Call;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_411 "SA ALT delete or keep" id "5ceab400-0000-4000-8000-00000000096b" {
|
||||||
|
body {
|
||||||
|
Fragment {
|
||||||
|
branches [@life_sa_412, @life_sa_413];
|
||||||
|
operator Alternative;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_400;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_412 "SA Left delete operand" id "5ceab400-0000-4000-8000-00000000096c" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "stop_requested";
|
||||||
|
occurrences [@life_sa_414];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_413 "SA Right empty operand" id "5ceab400-0000-4000-8000-00000000096d" {
|
||||||
|
body {
|
||||||
|
Branch {
|
||||||
|
guard "else";
|
||||||
|
occurrences [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_411;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_414 "SA Conditional delete receiver" id "5ceab400-0000-4000-8000-00000000096e" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange @life_sa_service;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_402;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_401;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_412;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_500 "SA 05 Unassigned receiver" id "5ceab400-0000-4000-8000-0000000009c4" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_510, @life_sa_511];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_501 "SA Controller lane" id "5ceab400-0000-4000-8000-0000000009c5" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_502 "SA Unassigned receiver lane" id "5ceab400-0000-4000-8000-0000000009c6" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
represented none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_510 "SA Create unassigned receiver" id "5ceab400-0000-4000-8000-0000000009ce" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Create;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_511 "SA Delete unassigned receiver" id "5ceab400-0000-4000-8000-0000000009cf" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_502;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_501;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_500;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_scenario life_sa_600 "SA 06 Self delete" id "5ceab400-0000-4000-8000-000000000a28" {
|
||||||
|
context none;
|
||||||
|
description "";
|
||||||
|
parent none;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
steps [@life_sa_610];
|
||||||
|
viewpoint Functional;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_lifeline life_sa_601 "SA Controller lane" id "5ceab400-0000-4000-8000-000000000a29" {
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
represented @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_occurrence life_sa_610 "SA Self delete controller" id "5ceab400-0000-4000-8000-000000000a32" {
|
||||||
|
body {
|
||||||
|
Exchange {
|
||||||
|
exchange none;
|
||||||
|
message_kind Delete;
|
||||||
|
receiver @life_sa_601;
|
||||||
|
reply_to none;
|
||||||
|
sender @life_sa_601;
|
||||||
|
values [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
parent @life_sa_600;
|
||||||
|
postcondition "";
|
||||||
|
precondition "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_input "SA Lifecycle service input" id "a4a6ecc0-3e12-51a5-b08b-199ad743525c" {
|
||||||
|
contract {
|
||||||
|
FunctionInput {
|
||||||
|
incoming_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_port sa_lifecycle_service_output "SA Lifecycle service output" id "b1aa66c4-0e8e-5091-beda-2b8d2005b688" {
|
||||||
|
contract {
|
||||||
|
FunctionOutput {
|
||||||
|
outgoing_items [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
description "";
|
||||||
|
legacy none;
|
||||||
|
parent @life_sa_10;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction life_oa_service "OA Lifecycle service" id "5ceab400-0000-4000-8000-0000000003f4" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_oa_10;
|
||||||
|
target @life_oa_11;
|
||||||
|
}
|
||||||
|
|
||||||
|
functional_exchange life_sa_service "SA Lifecycle service" id "5ceab400-0000-4000-8000-0000000007dc" {
|
||||||
|
description "";
|
||||||
|
items [];
|
||||||
|
source @life_sa_10;
|
||||||
|
source_port @sa_lifecycle_service_output;
|
||||||
|
target @life_sa_11;
|
||||||
|
target_port @sa_lifecycle_service_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// Preserve lifecycle note: 雨 · receiver.
|
||||||
|
// Geometry never changes these bytes.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"source_commit": "29084050a7c020e0fb488be37c6e1ab235769cc0",
|
||||||
|
"runtime_dll_sha256": "AA282FA47BB2C4E210CEA5E470CDBDCA58568F0BE0BC73E404D86AB9CD95EF6A",
|
||||||
|
"started_utc": "2026-09-11T15:54:23.8377438Z",
|
||||||
|
"working_directory": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\lifecycle-native-ui",
|
||||||
|
"arguments": [],
|
||||||
|
"isolated_localappdata": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\lifecycle-native-ui\\profile-29084050",
|
||||||
|
"executable": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\lifecycle-native-ui\\application-29084050\\arcadia-studio.exe",
|
||||||
|
"acceptance": "Development executable launched; native observation pending. Not a packaged-release acceptance.",
|
||||||
|
"pid": 4532,
|
||||||
|
"executable_sha256": "47332A6E6A0A496052754866A0863E5A08FE97AEB1D8A91C590A6AA5479035C2",
|
||||||
|
"build_status": {
|
||||||
|
"started_utc": "2026-09-11T15:33:29.7080352Z",
|
||||||
|
"exit": 0,
|
||||||
|
"log": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\ytr83r-desktop-native-api-correction.log",
|
||||||
|
"command": [
|
||||||
|
"cargo",
|
||||||
|
"build",
|
||||||
|
"--locked",
|
||||||
|
"--offline",
|
||||||
|
"-p",
|
||||||
|
"oa-desktop",
|
||||||
|
"--bin",
|
||||||
|
"arcadia-studio"
|
||||||
|
],
|
||||||
|
"seconds": 1160.0833972,
|
||||||
|
"completed_utc": "2026-09-11T15:52:49.8291375Z",
|
||||||
|
"name": "ytr83r-desktop-native-api-correction"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS oa-rename: complete Project/reachable sources/physical map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS same: all identities, fields, reachable source and physical bytes
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS oa-rename: complete Project/reachable sources/physical map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS oa-rename: complete Project/reachable sources/physical map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS same: all identities, fields, reachable source and physical bytes
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS baseline: complete Project/reachable sources/physical map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
PASS same: all identities, fields, reachable source and physical bytes
|
||||||
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 96 KiB |