//! Offline engineering help. Topics describe the shared model and real UI controls. //! //! This module owns only reading/navigation state. Application navigation and demo //! loading use normal App messages, so help never bypasses the model's draft guards. use crate::{ app::{Message, Page, Pending}, style, }; use iced::{ Element, Fill, Font, widget::{button, column, container, keyed_column, row, scrollable, text, text_input}, }; #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub enum Topic { #[default] FirstProject, Perspectives, Participants, Nesting, Capabilities, Exchanges, Chains, Scenarios, States, Interfaces, Transitions, SemanticReview, Artifacts, Source, ModelChecks, Recovery, Files, Server, } #[derive(Debug, Clone, Default)] pub struct State { pub query: String, pub topic: Topic, } #[derive(Debug, Clone)] pub enum Event { Search(String), Topic(Topic), } pub(crate) const ARTICLE_SCROLL_ID: &str = "engineering-guide-article"; impl State { /// Returns whether navigation selected a different article. pub fn update(&mut self, event: Event) -> bool { let previous = self.topic; match event { Event::Search(query) => { self.query = query; let matches = search(&self.query); if !matches.contains(&self.topic) && let Some(first) = matches.first() { self.topic = *first; } } Event::Topic(topic) => { self.topic = topic; // Related/contextual links always reveal the requested topic. self.query.clear(); } } self.topic != previous } } impl Topic { const ALL: [Self; 18] = [ Self::FirstProject, Self::Perspectives, Self::Participants, Self::Nesting, Self::Capabilities, Self::Exchanges, Self::Chains, Self::Scenarios, Self::States, Self::Interfaces, Self::Transitions, Self::SemanticReview, Self::Artifacts, Self::Source, Self::ModelChecks, Self::Recovery, Self::Files, Self::Server, ]; pub fn title(self) -> &'static str { match self { Self::FirstProject => "Build your first model", Self::Perspectives => "Operational & System Analysis", Self::Participants => "Participants, roles & work", Self::Nesting => "Ownership, allocation & views", Self::Capabilities => "Missions & capabilities", Self::Exchanges => "Exchanges & dataflow", Self::Chains => "Processes & functional chains", Self::Scenarios => "Scenarios & alternatives", Self::States => "States, modes & transitions", Self::Interfaces => "Data types, ports & interfaces", Self::Transitions => "Move between OA & SA", Self::SemanticReview => "Review semantic changes", Self::Artifacts => "Connect engineering evidence", Self::Source => "Textual editing & Git", Self::ModelChecks => "Read model checks", Self::Recovery => "Drafts & crash recovery", Self::Files => "Files & keyboard shortcuts", Self::Server => "Hosted models & REST API", } } fn category(self) -> &'static str { match self { Self::FirstProject | Self::Perspectives => "START HERE", Self::Participants | Self::Nesting | Self::Capabilities | Self::Exchanges => { "MODEL THE SYSTEM" } Self::Chains | Self::Scenarios | Self::States | Self::Interfaces => "DESCRIBE BEHAVIOR", Self::Transitions | Self::SemanticReview | Self::Artifacts | Self::Source => { "CONNECT YOUR WORK" } Self::ModelChecks | Self::Recovery | Self::Files | Self::Server => "REFERENCE", } } } struct Article { summary: &'static str, keywords: &'static str, sections: &'static [Section], related: &'static [Topic], destination: Option<(Page, &'static str)>, } struct Section { title: &'static str, body: &'static str, steps: &'static [&'static str], code: Option<&'static str>, } const fn explain(title: &'static str, body: &'static str) -> Section { Section { title, body, steps: &[], code: None, } } const fn walkthrough(title: &'static str, steps: &'static [&'static str]) -> Section { Section { title, body: "", steps, code: None, } } const fn example(title: &'static str, body: &'static str, code: &'static str) -> Section { Section { title, body, steps: &[], code: Some(code), } } fn article(topic: Topic) -> Article { match topic { Topic::FirstProject => { const { Article { summary: "Create a small operational model, then derive the system contribution. Use concrete names for work and keep your first diagram focused.", keywords: "getting started tutorial beginner quick start new example rescue coordinator allocation", sections: &[ walkthrough( "Create an operational context", &[ "Choose New in the top bar. Use Project properties to name the model and describe its purpose. Apply changes commits the property draft.", "On Architecture, choose Create architecture view. Architecture · allocated work shows work inside its performer.", "Expand the diagram's side palette to see tool names. Choose Entity, click its position in the diagram, enter Rescue coordinator and press Enter. Escape cancels the draft. Use Actor for an external participant such as a Vessel in distress. Right-clicking a blank area also offers Create Entity here.", "Right-click Rescue coordinator and choose Add activity. Enter Assess incident and press Enter to create the activity and its performer allocation together. Ordinary Activity creation in the palette leaves work unallocated; use + Relationship with Allocation to assign existing work. The allocated-work view shows work inside its performer.", "Use Add existing to place an element that is already in the model. The initial choices match this view; no search is required. Include already shown offers Locate for elements already placed here.", "Add a second activity for another participant. Select an activity, choose + Relationship, select Interaction and choose the compatible target. Name the exchange after what is conveyed.", "Visit Model checks to review unfinished work. Save all writes the accepted model and applied drafts to your chosen file.", ], ), explain( "Continue into system design", "Use Create System counterpart on an operational element to create a linked SA element. Choose System for the system under study or System actor for an external participant, then choose the result diagram. The original operational element stays in OA. The transition tutorial explains scope and reuse.", ), walkthrough( "Build a reviewable OA → SA baseline", &[ "State the mission and capability: for example, protect life at sea by coordinating a rescue. Link the mission to the capability and identify the participants that support it.", "Describe the normal operational Process and an exception Scenario using the activities and Interactions already in the architecture. Give the scenario an explicit Context capability and connect the process with Chain involvement.", "Review which operational work the System will support. Create or reuse its SA counterparts, then check the resulting System boundary, external actors, leaf Function allocations and exchanged payloads.", "Refine the system Functional chain, Scenario and State machine. Specify data contracts and entry/do/exit work where they clarify responsibilities. Link requirements, analyses or tests to the particular records they support.", "Read Model checks, review changes through counterpart traces, and inspect both perspectives. Save the complete source project and review its Git diff as one engineering milestone.", ], ), explain( "Explore a worked model", "Open the coastal rescue example to inspect allocation, decomposition, repeated scenario messages, operating modes, typed interfaces and artifact references. Loading the example uses the normal unsaved-work prompt. It replaces the current project only after that workflow completes.", ), ], related: &[ Topic::Perspectives, Topic::Participants, Topic::Nesting, Topic::Transitions, Topic::ModelChecks, ], destination: Some((Page::Diagram, "Open Architecture")), } } } Topic::Perspectives => { const { Article { summary: "Operational Analysis asks what people and organizations need to accomplish. System Analysis asks what the system must do within that operation.", keywords: "oa sa arcadia capella system architecture operational need boundary abstraction actor system of interest", sections: &[ explain( "Operational Analysis: understand the need", "Describe entities and actors, their activities, roles, interactions and operational capabilities without assuming a particular technical solution. An activity such as Assess incident states work in the operation; it does not imply which software module implements it.", ), explain( "System Analysis: define the contribution", "Describe one System under study, external System actors, Functions, functional exchanges, missions and system capabilities. A participant's operational activity may be supported by several system functions or may remain outside the system boundary. Review that engineering decision explicitly.", ), explain( "Switching perspective does not transition the model", "The 01 Operational and 02 System controls change the visible perspective and creation choices. To represent an element in the other layer, use Create System counterpart or Create Operational counterpart. Both elements remain linked by a trace.", ), explain( "Keep the boundary explicit", "Separate work performed by the System from work performed by external actors. Allocate leaf functions to the responsible participant. Logical and Physical Architecture belong to later design layers; a system-level function is not a software class or a physical component.", ), ], related: &[Topic::FirstProject, Topic::Capabilities, Topic::Transitions], destination: Some((Page::Catalog, "Open Model explorer")), } } } Topic::Participants => { const { Article { summary: "Represent who participates separately from the work they perform. Decomposition and responsibility answer different engineering questions.", keywords: "entity actor participant role realization assignment leaf activity function performer decomposition responsibility", sections: &[ walkthrough( "Create and allocate work", &[ "Choose the Operational or System perspective. Create participants using Entity/Actor in OA or System/System actor in SA.", "In the diagram's side palette, choose Activity in OA or Function in SA, click its position, enter a name and press Enter. Escape cancels without creating an element. Select a card and press F2 to rename it in place. For semantic decomposition, use Model explorer, select existing work and use + In selected to create smaller work units.", "Right-click a participant and choose Add activity or Add function to create new work and its allocation together. To assign existing leaf work, use + Relationship with Allocation from the participant to the work. Model explorer also offers + Allocated when a participant and the appropriate work kind are selected.", "Choose Architecture · allocated work to review responsibilities. Choose Functional dataflow to review the activity/function hierarchy and its exchanges independently.", ], ), explain( "Operational roles", "A Role describes a responsibility independently of a particular entity. Role realization links an Entity or Actor to a Role. Role assignment links a Role to an Activity. These relations express role responsibilities; an Allocation identifies the concrete performer of leaf work.", ), explain( "Name work as an action", "Prefer Assess incident or Deliver electrical power for work, and Rescue coordinator or Power controller for participants. A meaningful description records assumptions, input/output expectations and the decision behind an allocation.", ), explain( "Creation choices are constrained", "+ In selected is enabled only for a legal semantic parent. + Allocated appears when the selected participant can perform the chosen work. Pickers show compatible endpoints. If a control is unavailable, check the active perspective, selected owner and element kind.", ), ], related: &[Topic::Nesting, Topic::Exchanges, Topic::Capabilities], destination: Some((Page::Catalog, "Open Model explorer")), } } } Topic::Nesting => { const { Article { summary: "A diagram is a view of the model. Its boxes, positions and nesting reveal selected relationships; moving a box does not redefine engineering ownership.", keywords: "containment semantic parent hierarchy nested nesting allocation deployment diagram placement arrange fit fold hide zoom resize routing connection bend waypoint detour label reset selective visibility category categories coverage process context duplicate copy items", sections: &[ explain( "Three things to distinguish", "Semantic parent records ownership: an activity contains subactivities, or a scenario owns occurrences. Allocation records who performs leaf work. A placement records where one element appears in a diagram. One element can have placements in several views without becoming several semantic elements.", ), walkthrough( "Browse a large model", &[ "Open Model explorer. Choose a group, then a container to see its direct children. Breadcrumbs return to its owner or group; Allocated work is a separate list of responsibilities.", "Search names, kinds or @aliases in the left outline. Matching elements retain their ancestor context; the main browser lists up to 100 results per page. Select a result to open its details and clear the search.", "Use Collapse all to fold the outline and Locate to reveal the selected element. Search keeps matching ancestry open; clear the search before folding individual groups or containers.", ], ), walkthrough( "Choose and edit the relationship you mean", &[ "Use the diagram selector or + View to work with a focused diagram. View properties edits its name, perspective, projection and Saved view visibility. Apply changes commits the view settings.", "Choose Containment for semantic children, Architecture · allocated work for responsibilities, Functional dataflow for work and exchanges, or Missions & capabilities for outcomes. All relationships is a broader exploration view; OA ↔ SA traceability displays counterparts.", "Select an element and edit Semantic parent to change ownership. Use an Allocation relationship to change responsibility. Apply changes validates the semantic change.", "Drag nodes to arrange the current view, scroll to zoom and use Fit to frame its contents. Arrange changes diagram geometry. Export SVG produces a shareable view.", "Select a diagram node to access Placement, Fold / unfold and Hide. Hide removes its view placement; Delete from model removes model records and requires review of the affected references.", ], ), explain( "Keep diagrams readable", "Use separate views for responsibility, decomposition and cross-layer coverage. Place existing elements with Show in this diagram. A placement's Diagram field can relocate it while preserving its identity and artifact references. Do not duplicate a semantic element merely to show it in another view.", ), walkthrough( "Save a focused exchange view", &[ "To preserve the current presentation, right-click empty diagram space and choose Duplicate view. The copy has independent placements, routes and visibility settings while representing the same model elements and relationships. View-specific artifact links stay with the original view.", "Open View properties. Under Work exchange categories or Participant exchange categories, uncheck All, including uncategorized exchanges, then Include the categories you need. Selected categories combine by union; none selected shows none of that family. Work means OA Interactions or SA Functional exchanges; participant means OA Communication means or SA Boundary exchanges, including supported legacy records.", "Work exchange coverage across the whole model offers All work exchanges, Used in behavior and Outside behavior. This uses explicit process, chain and scenario occurrences, including reuse. To focus on particular behavior, uncheck All work contexts and Include those processes, chains or scenarios. Context and coverage intersect with categories; no selected contexts means no work lines.", "Choose Exchange labels: Names, Items or Names and items. In Individual lines and omission reasons, search a name or alias and check Hide to retain an explicit hide. Unchecking Hide removes only that reason; other settings or missing endpoints may still omit the line. Clear all explicit hides removes the manual hide list.", "Apply changes and inspect the result. To restore all eligible lines, enable All relationship line kinds, both category All choices and All work contexts, choose All work exchanges, and clear explicit hides. Empty relationship-kind selection shows no lines. These settings do not remove nodes or model facts; a line still needs endpoints eligible for the projection.", ], ), explain( "Read allocation summaries and ports", "A dashed parent activity/function with an italic name and SUMMARY heading groups leaves assigned to the same performer. It has no direct allocation of its own; its inspector explains the leaf performer. Small ports belong to the boundary of their visible owner and are selectable model elements. Folding a container hides its inner work and ports; summary exchange lines do not invent new ports on the container.", ), walkthrough( "Route connections around the model", &[ "Select an architecture connection and drag a segment to add a bend. Right-click a line for Add bend here; right-click a circle for Remove bend. With the canvas focused, Insert adds a bend and Delete removes the selected bend. Drag the label independently. Escape cancels a gesture; Undo reverses one completed drag. Shift+F10 opens the selected diagram item's context menu; use arrow keys and Enter to choose an action.", "Automatic orthogonal routing avoids cards and container headers where space permits. Amber connections need attention: move an obstructing element or a bend. In Select / move, drag a port to any border of its owner. Manual positions are per diagram; right-click Reset automatic position restores connection-aware placement. Ports remain attached to their actual owners. Manual routes are saved separately for each diagram and appear in source, API and SVG export.", "Right-click a connection and choose Reset routing to clear its pins and label position in this view. The Properties Actions menu provides the same commands. Arrange preserves pins. Moving one endpoint keeps pins fixed; moving a container carrying both endpoints translates their pins. When folding promotes an endpoint, expand it again to resume and edit its saved route.", ], ), explain( "What nesting does not imply", "Containment in an allocation projection expresses a performer relationship. It is not a physical deployment statement. Physical deployment is a different architectural concern; do not infer hardware installation from an OA/SA box layout.", ), ], related: &[Topic::Participants, Topic::Perspectives, Topic::Artifacts], destination: Some((Page::Diagram, "Open Architecture")), } } } Topic::Capabilities => { const { Article { summary: "Capabilities describe an ability to achieve an outcome. Missions explain the purpose served by those abilities. Both Operational and System Analysis have missions, capabilities and participants; activities and functions describe supporting work.", keywords: "capability mission outcome operational capability system capability involvement contribution trace coverage use case", sections: &[ walkthrough( "Connect outcomes to the work", &[ "Create an OA Capability such as Protect life at sea. Describe the operational outcome and the conditions in which it matters.", "Select a relevant participant, role or activity. Use + Relationship → Capability involvement and choose the capability as the target.", "Create an Operational mission in OA, or a Mission in SA. Select it and choose Exploit capability to connect it to a capability in the same perspective. Involve participant associates an entity or actor with the mission.", "Connect System participants/functions to the capability using Capability involvement. Connect a Process or Functional chain to a capability using Chain involvement.", "Review OA/SA counterpart links in Traceability and read Model checks for incomplete coverage. A named capability alone does not demonstrate that the necessary behavior exists.", ], ), walkthrough( "Include, extend or specialize a capability", &[ "Select a capability in the diagram or explorer, then open Actions at the top of its properties. Include capability identifies behavior that is always part of it; choose the included capability as the target.", "Extend capability connects this extending capability to the extended capability. Enter the condition under which the extension adds behavior. An empty condition remains editable and appears in Model checks.", "Specialize capability connects a specific capability to a more general one. Inclusion and specialization cannot form cycles.", "The relationship preview names both ends and generates a readable name. Apply adds one undoable change; Cancel preserves the model. Include and extend labels remain visible in the diagram.", ], ), explain( "Use scenarios as concrete evidence", "A scenario illustrates normal response, degraded operation or an exception. In its Properties, choose Context to name the capability it supports in the same perspective; record assumptions and pre/postconditions too. Connect a Process or Functional chain with Chain involvement. Descriptive prose explains the intent, while these explicit references make the context inspectable.", ), explain( "Avoid implementation names at this stage", "Protect life at sea is a capability. Coordinate rescue is work. Send a JSON request is usually a design choice. Start with the engineering obligation and add technical detail when the perspective calls for it.", ), ], related: &[ Topic::Perspectives, Topic::Chains, Topic::Scenarios, Topic::Transitions, ], destination: Some((Page::Catalog, "Open Model explorer")), } } } Topic::Exchanges => { const { Article { summary: "An exchange says what passes between work or across a participant boundary. Its payload, endpoints and allocation make that statement precise.", keywords: "interaction functional exchange boundary exchange communication mean participant directed bidirectional category categories flow information material energy event source target port exchange allocation", sections: &[ walkthrough( "Create a typed exchange", &[ "Create the source and target activities/functions in one perspective. Give each an engineering name and allocate leaf work to its responsible participant.", "Choose Interaction or Functional exchange in the diagram palette. Click the source, then a highlighted compatible target, or drag between them. Functional exchanges connect output ports to input ports; function cards create or reuse real ports in the same transaction. Middle-button drag pans while keeping the source; arrow keys also pan when the drawing canvas has focus. Click empty space to restart, or Escape to cancel.", "Create an Exchange item under Data & interfaces. Choose its nature (Information, Material or Energy) and its mechanism (Flow, Event, Operation or Shared data). Describe the payload independently of the connection.", "Select the exchange in Model explorer or the diagram. Edit its properties to reference the appropriate exchange items and available ports; Apply changes validates the complete record.", "In System Analysis, choose Boundary exchange and connect compatible component ports. Flow goes from Out to In; Standard is unoriented. Select / move returns to arranging. Exchange allocation associates a boundary exchange with the functional exchange it carries.", ], ), explain( "Flow and sequence are different", "A dataflow graph shows possible dependencies between activities/functions. It does not by itself impose an execution order. Use a Process, Functional chain or Scenario when you need to describe an ordered path, repeated exchanges or alternatives.", ), explain( "Use engineering payloads", "Choose Information for data such as a distress report, Energy for delivered power and Material for a transferred substance. Mechanism separately describes how it is exchanged: an Event is a notification, an Operation has parameters, and a Flow carries values or a physical quantity. Data types and fields make payloads precise. Artifact links can point to interface-control documents.", ), explain( "Operational and system boundaries", "An OA Interaction connects activities. A Communication mean connects operational Entities or Actors directly; it requires no system ports. In Operational Analysis, choose Communication means and connect its participants. Its properties hold carried items and direction; the relationship form offers Directed source → target (unchecked: bidirectional). Existing OA Boundary exchanges with Legacy ports remain editable for compatibility.", ), explain( "Relate operational transport and concerns", "Use Exchange allocation to associate a Communication mean with the Interactions it carries. Allocate each activity to its actual performer and check that those performers match the communication endpoints and direction. Create an Exchange category at the perspective root, choose Work exchanges or Participant exchanges in its properties, and Include existing exchange members. Categories name reusable concerns for view filtering; they do not change endpoints, allocate work or imply execution. A category may be empty, and an exchange may belong to several categories.", ), ], related: &[ Topic::Interfaces, Topic::Chains, Topic::Scenarios, Topic::Artifacts, ], destination: Some((Page::Catalog, "Open Model explorer")), } } } Topic::Chains => { const { Article { summary: "An operational Process or system Functional chain connects contributions of existing work and exchanges, including alternatives and reusable procedures. It explains how work contributes to an outcome.", keywords: "process functional chain ordered occurrence sequence path involvement dataflow exchange repeated steps caller contextual qualified assembly common work focus reuse", sections: &[ walkthrough( "Describe a path through exchanges", &[ "First create the activities/functions and their exchanges. Check the source and target work for every exchange.", "Open Behavior & scenarios. Create Process in OA or Functional chain in SA using Create element and + At root.", "Select that behavior in the page's selector. 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.", "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.", "Use Chain involvement from the Process/Functional chain to the capability it supports. Read Model checks for unfinished or disconnected paths.", ], ), explain( "Choose the right behavioral description", "A chain concentrates on a route through existing exchanges. A scenario can also carry actions, state observations, lifelines and nested fragments. The architecture diagram remains a view of the possible relationships rather than an execution trace.", ), explain( "Review the path as an engineer", "Check that exchanges express meaningful handoffs and that the links and branch guards support the selected outcome. Use Tools → Reuse to involve an existing process or chain as a nested reference. For an imported ordered exchange path, Append exchange… continues that path; Convert ordered path to explicit links exposes its implied links for topology editing. Conversion preserves the original order in one undoable change.", ), walkthrough( "Connect a particular use of a procedure", &[ "Choose Focus canvas for more working space; Restore panels brings the explorer and properties back. Select a contribution inside a reusable preview, then open Selection details to read its complete caller and reference identity.", "In Selection details, use Expand nested reference or Fold nested reference to inspect that instance. Open source or Open contribution definition visits the shared definition; Back to caller returns to the original caller context.", "Open Tools → Qualified link / assembly… and choose its source and target. Use Enter reference to open an instance's contributions, Up one reference for its caller scope, or Root for the link owner. Search and page through all contributions in that scope, then select the exact endpoint. The first and second uses of a procedure remain different choices even when their work has the same name.", "Choose Precedence to describe order and its guard, Exchange assembly to identify a work exchange between the contributions, or Common work incidence to state that both use the same work. Select the engineering identity when required, then Apply link.", ], ), explain( "Keep caller intent separate from the definition", "A link into the first assessment's second contribution belongs to that caller. It does not change the shared assessment or its second use, and it does not delay the first contribution by targeting the whole procedure. Common work records shared identity without merging executions. Model checks identify the particular caller path and report incomplete analysis when expansion cannot be completed.", ), ], related: &[Topic::Exchanges, Topic::Capabilities, Topic::Scenarios], destination: Some((Page::Behavior, "Open Behavior & scenarios")), } } } Topic::Scenarios => { const { Article { 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", sections: &[ walkthrough( "Create a scenario", &[ "Open Behavior & scenarios, choose Scenario under Create element and add it at a legal root/owner. Select the scenario in the behavior selector.", "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.", "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.", "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.", "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.", "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( "Describe alternatives", &[ "Choose Fragment 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.", "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.", "Inspect the scenario projection and Model checks. State exactly what each guard means, including units and assumptions where relevant.", ], ), walkthrough( "Invoke an interface operation", &[ "In an SA BoundaryInterface scenario, create or edit a Call or Asynchronous message. Choose its boundary exchange and actual sending and receiving lifelines first. The receiving Standard component port must provide the interface; the sending port must require a compatible interface.", "Enable Invoke a specific interface operation and choose the exact eligible allocation. Two parameterless operations on one channel remain distinct. Choose Keep operation unresolved when the operation decision is unfinished; existing value expressions remain available.", "For a Reply, inspect Reply operation from Call. It derives the selection from that particular Call and cannot be selected independently. Enter values for the selected operation's call or reply parameters; incompatible values must be corrected explicitly.", "When transitioning that invocation to OA, explicitly choose to omit the technical operation and enter the engineering rationale in the preview or semantic review row. The SA source keeps its operation identity.", ], ), explain( "Conditions are engineering statements", "Guards, actions and effects document behavior; a drawn scenario does not execute code or prove timing. Add evidence references when a simulation, test or analysis establishes a stronger claim.", ), explain( "Keep uses distinct from definitions", "Several occurrences can refer to one exchange without duplicating it. Edit occurrence properties for this message's sender, receiver and value expressions; edit the exchange for the shared endpoints and payload. A Reply refers to a particular Call, not merely a similar name. Reordering across fragment operands or changing lifeline bindings needs an explicit structural edit, not an accidental drag across a boundary.", ), ], related: &[ Topic::Chains, Topic::States, Topic::Exchanges, Topic::Artifacts, ], destination: Some((Page::Behavior, "Open Behavior & scenarios")), } } } Topic::States => { const { Article { summary: "Describe the conditions in which the operation or system behaves differently. States and modes belong to regions within a state machine.", keywords: "state machine mode region initial final transition trigger guard effect entry exit do availability degraded nominal diagram draw reconnect local internal timed fold unfold placement pan zoom undo", sections: &[ walkthrough( "Build a state machine", &[ "In Behavior & scenarios, create or select a State machine, then choose Create diagram. This 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 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.", "Select a drawn transition and drag its endpoint handle to reconnect it. Its right-click menu also offers Reconnect source and Reconnect target; choose one, then click a compatible vertex.", "Right-click a state or the background for New transition with details… to create Local, Internal or timed behavior. Right-click an existing transition and choose Edit transition to edit its kind, triggers, guard and effects. EntryPoint outgoing transitions use Local. Apply accepts the complete form; invalid drafts remain available for correction.", ], ), explain( "Navigate and recover a view", "Middle-button drag pans; scroll or use −/+ to zoom, and Fit frames the diagram. Right-click a container for Fold contents or Unfold contents, or choose Placement properties for exact coordinates and size. Hide from this diagram removes only its placement; Show an element not yet in this view restores missing elements. Escape cancels a drawing gesture. Undo restores each completed gesture or menu edit, and Model checks identifies unfinished structures.", ), explain( "One saved diagram across editors", "Each state view stores a reference to its state machine and its own placements. Text, JSON and REST edit those same diagram records, including the persisted machine context; saving and reopening retains them. Merely opening the behavior page does not create a diagram. State-view edits preserve other diagram layouts.", ), explain( "Availability and state actions", "Availability links a participant or behavior to a State/Mode in which it is available. State action links a State/Mode to existing Activity/Function work and records Entry, Do or Exit. The saved state view shows those named action rows above internal transitions and nested regions. Select a row to edit its actual State action relationship. The linked work remains in its own semantic owner; the row does not contain or copy it. Free-text entry/do/exit notes remain separate engineering prose.", ), walkthrough( "Add timing without losing an existing transition", &[ "Select the transition and choose Edit transition. Its current endpoints, kind, existing triggers, guard and effects populate the form.", "Choose Add a time event with this transition, select After for a delay or At for a time, then enter a time expression such as 30 s. This adds a typed Event to the trigger alternatives; it does not replace existing triggers.", "Review the complete transition before Apply. The event and transition update are one undoable change. If the form is rejected, correct the retained values; a rejected candidate creates no orphan Event.", "Save the entry and any included source files. On reopening, check the transition's trigger list and the Event expression. An expression documents timing intent; it is not evaluated by a simulator.", ], ), explain( "Keep conditions testable", "Prefer a guard such as battery_level < reserve_threshold over a vague phrase such as low battery, and define the quantities elsewhere in the model or linked evidence. A state diagram documents the intended behavior; it is not a running state-machine simulator.", ), ], related: &[ Topic::Scenarios, Topic::Participants, Topic::Recovery, Topic::Artifacts, ], destination: Some((Page::Behavior, "Open Behavior & scenarios")), } } } Topic::Interfaces => { const { Article { summary: "Define exchanged payloads independently of the ports and participants that carry them. Reuse the same data definitions in related exchanges.", keywords: "data interface port direction input output inout return contract provision requirement exchange item enum enumeration record primitive field value type multiplicity minimum maximum bounds default unit specialization inheritance association storage reference recursive path transmission acquisition protocol", sections: &[ walkthrough( "Define a reusable payload", &[ "Open Data & interfaces and create an Exchange item. Choose Information, Material or Energy for nature; choose Flow, Event, Operation or Shared data for mechanism. Keep Unspecified when the engineering decision is still open.", "Create a Data type for a reusable definition. Choose Boolean, Integer, Real, String, Enumeration or Record. Numeric bounds are inclusive; String bounds count Unicode characters. A typed default must satisfy those bounds or enumeration literals. Units annotate quantities without converting them.", "Create fields within an Exchange item or Record type. Set Value type, minimum/maximum count, unit and optional default. A default can be absent, explicitly empty when minimum is zero, or a list of typed scalar values. A type default describes one element; it does not create a required collection.", "Select the exchange and reference the payload items. Apply changes and check the result in Model checks.", ], ), explain( "Operation parameters and message values", "An Operation's direct fields are parameters. In and InOut supply call values; Out, InOut and Return supply reply values, from the provider's perspective. There is at most one Return parameter. Qualified paths distinguish origin.latitude from destination.latitude when both use the same Coordinate record. Nested fields inherit the parameter direction. Message expressions are annotations, not evaluated values.", ), explain( "Reuse, ownership and references", "A Record or Exchange item can specialize one compatible base. Inherited fields keep their original owner and identity; edit the base to change them. Value fields compose a payload. Reference fields refer to separately identified Record objects and stop payload expansion. Optional recursive values and reference cycles are valid; a mandatory value cycle warns that no finite complete value exists. Data associations describe named reference ends, multiplicities and navigation between Record types without creating contained fields.", ), walkthrough( "Allocate an item to interfaces", &[ "Create an Interface and allocate an Exchange item. Select its allocation row to edit transmission, acquisition and order; Open item navigates to the shared payload definition.", "Two interfaces may allocate the same item with different protocols. Attach CAD, code or document evidence to the allocation when it concerns that particular contract.", "Removing an allocation keeps the shared item and other allocations. If a live boundary depends on it, first revise that boundary contract. Undo restores an accepted removal.", "An Interface may specialize one base and inherit its allocations. Duplicate allocation of an already inherited item is rejected. A specialized item satisfies its base contract; a base item does not satisfy a more specific contract.", ], ), walkthrough( "Connect participant boundaries", &[ "In System Analysis, select a System or System Actor and choose Add port in Model explorer or Properties. Name it, choose Standard or Flow, then Create port. Standard ports describe interfaces; Flow ports have their own input/output orientation. Functions use separate input/output port actions.", "Use Interface provision and Interface requirement relationships to state who provides or needs the contract.", "Select a visible component port and choose Draw boundary exchange, then click or drag to a highlighted compatible port. The diagram palette offers the same tool. If the endpoints are outside the diagram, Configure boundary exchange opens the form with owner names and aliases. Create the matching port first if needed. Exchange orientation is separate from Flow port orientation. Exchange allocation associates carried functional exchanges with the boundary connection.", "Link the detailed interface-control document, CAD connector or schematic through an Artifact reference when an external artifact supplies additional evidence.", ], ), explain( "Preserve meaning across perspectives", "Shared kinds such as Data type and Interface still belong to an engineering perspective. Their Engineering perspective property is explicit. Use counterpart traces for separate OA and SA definitions that correspond; do not assume that similar names create a trace.", ), ], related: &[Topic::Exchanges, Topic::Artifacts, Topic::Transitions], destination: Some((Page::Data, "Open Data & interfaces")), } } } Topic::Transitions => { const { Article { summary: "Create or reuse counterpart records across OA and SA through a reviewable preview. Both perspectives keep their identities and remain linked.", keywords: "transition counterpart bidirectional mapping realization drift conflict merge keep target take source baseline scope dependency reverse update trace reuse", sections: &[ walkthrough( "Review a counterpart transition", &[ "Select an operational element and choose Create System counterpart. For a system element, Create Operational counterpart starts the reverse direction. The source stays in its original layer.", "Choose Include: Selected only, Required references or Whole subtree. Required dependencies must have compatible counterparts before the plan can apply.", "For each source, choose Create new counterpart or reuse an existing target. For an operational participant, explicitly choose System for the system under study or System actor for an external participant. Use Show result in to choose a compatible diagram; Automatic selects an architecture view for ordinary elements.", "Read the proposed changes and conflicts. For a descriptive conflict, choose the source value, keep the target value, or enter a merged value. Changing the target/kind requires reviewing that target's decisions again.", "Apply only after every blocking decision is resolved. The accepted plan is one undoable model change; Cancel preserves the current model.", ], ), explain( "Review drift deliberately", "On an existing trace, choose Review counterpart changes… to compare typed content and prior observations. Choose Keep for the destination, Take for the source, or Set final value / Set final kind and payload where offered. Review mapped references and proposed changes, then choose Apply reviewed changes. Independent edits may be intentional; keeping a value records that decision. A manual trace records correspondence without propagating properties.", ), explain( "Know what reconciliation changes", "Semantic review supports structural and scalar changes, typed constructors, individual collection rows, explicit ordered collections, existing attachments and artifact links. Several trace rows can contribute to one combined destination proposal. Candidate validation checks the complete proposed model before one undoable transaction. The Review semantic changes topic explains included rows, mapped references and each explicit attachment decision.", ), explain( "Avoid duplicate counterparts", "Check an existing trace before requesting a new counterpart. Several engineering records can correspond, but each mapping should have a reason. Reverse transitions also use the trace to reuse the intended record rather than silently replacing the original.", ), ], related: &[ Topic::Perspectives, Topic::Participants, Topic::Capabilities, Topic::Recovery, Topic::SemanticReview, ], destination: Some((Page::Traces, "Open Traceability")), } } } Topic::SemanticReview => { const { Article { summary: "Review what changed between corresponding OA and SA records. Choose the engineering result explicitly and retain the reasoned correspondence for later changes.", keywords: "semantic drift review keep take final candidate previously reviewed history reset reference pairs annotation qualified field path binding lifeline membership order up down reconcile steps fragment branch direction multi-source batch", sections: &[ walkthrough( "Compare the right records and direction", &[ "From an existing counterpart trace, choose Review counterpart changes…. Confirm the OA → SA or SA → OA direction and select the intended trace / review history. Each correspondence and direction has its own retained row draft; opening another row does not discard the first row's choices.", "In Properties, select a fact. Compare Previously reviewed with Current on both sides. Unreviewed means there is no prior observation; Both changed means both sides changed since that observation, not that one side is automatically wrong.", "Choose Keep for the destination, Take for the source, or Set final value…. For a kind change, Set final kind and payload… reviews the complete replacement and its affected references. Leave unselected makes no decision for that fact.", "Read Reference mapping and Additional changes before Apply reviewed changes. A missing or ambiguous correspondence must be resolved explicitly. Apply commits the validated model and the reviewed history together; Undo restores both.", ], ), explain( "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.", ), walkthrough( "Review one message value or lifeline binding", &[ "Open Contents and order and choose the exact identities on both sides. For a message value, browse the enclosing record fields as well as the final field: origin.latitude and destination.latitude are distinct addresses.", "For a binding, choose the referenced lifeline key and its compatible local lifeline. Check which scenario owns each side. Keep or Take concerns that row, not all bindings in the scenario.", "Use Set final value… to enter the destination expression or local lifeline. Include in destination collection controls that row's presence. Unchecking removes the entry without deleting its referenced engineering object.", "If a row key changed, review the old entry's removal and the new entry's addition separately. A missing source identity is not the same decision as an identified source entry that has been deleted. Read the proposed value before applying.", ], ), walkthrough( "Review membership and sequence order separately", &[ "Review which occurrences belong to Steps, or which operands belong to Fragment branches, using Contents and order. This changes membership; it does not silently reparent or create the referenced records.", "Choose Review final order… or the corresponding order fact in Properties. Keep retains destination positions. Take uses mapped source order when it can preserve destination-only members without an ambiguous placement.", "For an explicit result, choose Set final value…, then use Up and Down. If you change membership afterward, choose Reconcile with selected membership: surviving positions are retained, removed members leave the list, and new members append for you to position.", "Every surviving destination member must appear exactly once. Resolve an ambiguous insertion with an explicit final order; do not remove an engineering contribution merely to make the preview pass.", ], ), explain( "Review attached records and external evidence", "In Owned elements and relationships, choose exact OA and SA child or relationship identities, then Use exact attachment pair. Review a child's parent or a relationship endpoint independently; creating a new child or relationship requires its explicit transition mapping. In Evidence links, pair the exact links and choose Review this exact artifact pair. Presence concerns attachment to the reviewed owner. Taking an identified source absence can delete the destination link; a destination-only link is retained. Reserve a new destination counterpart only when one is needed, then explicitly review Presence and all seven metadata facts before the combined Apply. The reservation creates nothing by itself.", ), explain( "Preserve intentional differences and useful history", "Keep can acknowledge a deliberate difference without overwriting the destination. Reset this exact previous observation clears only the chosen review history; it does not restore an old model value. Overlapping row addresses may require an explicit history reset. Review those messages rather than clearing unrelated observations. Pending review choices and raw values can be recovered after an unexpected exit, but the plan is recomputed against the recovered accepted model.", ), walkthrough( "Review several sources together", &[ "Use Add linked object, then Review object. Include this object determines whether its decisions contribute to the shared preview. The object selector switches the object you are editing; excluded rows retain their drafts.", "Review each included source's contribution to the destination. Compatible decisions combine; contradictory Keep, Take or final-value decisions must be resolved explicitly. Read the combined conflicts, Reference mapping and Additional changes before Apply reviewed changes. One Apply commits the combined model and review histories; one Undo restores them.", "Only one trace history per source can be included. Some reverse correspondences cannot share the core reference mapping; the preview explains the conflict. Exclude a row or choose a distinct correspondence without discarding its draft. Switching direction retains the other direction's choices for later review.", "Recovery retains included and excluded row drafts and raw final values, then recomputes the plan against the recovered accepted model. Refresh preview is not automatic synchronization and does not authorize changes to attached records or artifacts.", ], ), ], related: &[ Topic::Transitions, Topic::Interfaces, Topic::Scenarios, Topic::Recovery, ], destination: Some((Page::Traces, "Open Traceability")), } } } Topic::Artifacts => { const { Article { summary: "Connect model decisions to external engineering evidence using stable references to code, CAD, schematics, documents and other artifacts.", keywords: "artifact traceability evidence code cad schematic electrical docx requirements revision fragment uri git commit owner placement document", sections: &[ walkthrough( "Link an artifact", &[ "Select the model record the evidence concerns. This may be an element, relationship, diagram, placement or other supported persistent record.", "Choose + Artifact reference in Properties, or + Artifact in Traceability. Name the reference so another engineer understands its purpose.", "Enter the Artifact URI, optional external Revision and Fragment. Use a commit or document revision when a reproducible reference matters; the fragment identifies a particular part of the artifact.", "Choose the relation to the artifact: References, Satisfies, Verifies or Implements. Add a description explaining the evidence and its limits. Apply changes, then inspect the owner and reference in Traceability.", ], ), explain( "Find existing evidence", "In Traceability, choose Development artifacts to show artifact links directly. Search by the link's name or alias, its owner, URI, revision or fragment. Layer traces shows OA/SA correspondences; All shows both lists. Filtering changes only what is shown and preserves the selected record and unfinished property input.", ), explain( "A reference is not an attachment", "The model preserves the author-provided URI, revision and fragment. Loading or saving a model does not fetch, execute or verify an external artifact. A path on one workstation may not exist on another; choose team-resolvable locations and identifiers.", ), explain( "Choose the right owner", "Link semantic evidence to the element or relationship it supports. Link view-specific evidence to a diagram or placement. A diagram node's placement identity remains distinct from its represented element, so the reference can express the intended scope.", ), explain( "Automation uses the same records", "A CAD or version-control plugin can query and edit artifact links through the REST API. Its update should preserve stable model IDs and use the server's conditional write contract to avoid overwriting another engineer's work.", ), ], related: &[Topic::Source, Topic::Server, Topic::Nesting], destination: Some((Page::Traces, "Open Traceability")), } } } Topic::Source => { const { Article { summary: "The text editor and graphical forms edit the same model. Source projects preserve file organization and comments for human review, automation and version control.", keywords: "text textual source language syntax highlighting lint parser git aliases uuid identity rename diff merge restructuredtext spec canonical parity", sections: &[ walkthrough( "Edit and review source", &[ "Open Textual model and expand folders to choose a source file, or choose Actions > Open source in Properties to locate the selected record. Switching files preserves each draft. Collapsed folders show modified-file and diagnostic counts; Locate reveals the active file. Right-click a file for its actions.", "Use Find / go to, Ctrl+F or Ctrl+G to navigate. Literal search is case sensitive. Enter/F3 selects the next match, Shift+F3 selects the previous match, and results wrap. Go to line accepts the displayed one-based line number.", "Edit the draft. Syntax highlighting distinguishes source structure; live lint reports errors with line and column information.", "Choose Apply all to parse and validate all reachable included files. A successful application is one model change; graphical views and Properties then reflect it. Finish or revert source drafts before editing graphical properties.", "If parsing or validation fails, the accepted model remains unchanged and the source draft is preserved. Correct the reported location or use Revert draft to restore the accepted source.", "Save all writes the entry and its included UTF-8 files. Review the Git diff before committing, including relationships, traces and diagram changes that belong to the same engineering edit.", ], ), explain( "Names, aliases and IDs serve different purposes", "The displayed name is engineering prose. A stable @alias is the readable source reference and need not change when the name changes. UUID identity distinguishes the persistent record. Preserve IDs when revising the same engineering object; changing identity is a deliberate operation that affects references.", ), example( "Readable references", "For example, renaming an activity does not require renaming @assess_incident. A related record can still refer to that alias.", "source @receive_distress;\ntarget @assess_incident;", ), explain( "Use one contract", "The normative specification is docs/language.rst in the distribution. It defines source grammar and supported graphical semantics together. Text preserves semantic records, diagram placements, ordered occurrences, traces and artifact references. Graphical edits preserve unaffected source formatting and comments; accepted model Undo/Redo also restores the exact source files. Comments and source-file organization are not part of JSON export.", ), example( "Keep included files with their entry", "An include is relative to the file that declares it. Put declarations in the included file and keep one project entry. A newly created graphical record may live in another source file; use Open source to locate it and include every affected file in your review and save.", "// Inside the project declaration:\ninclude \"oa/operations.arcadia\";\ninclude \"sa/model.arcadia\";", ), explain( "Resolve source-control conflicts before applying", "Ordinary Git conflict markers are not Arcadia syntax. Resolve the text merge, preserve intended UUIDs/references, then apply and inspect Model checks. The editor does not decide which engineer's model change is correct.", ), ], related: &[ Topic::Recovery, Topic::Files, Topic::Artifacts, Topic::Server, ], destination: Some((Page::Text, "Open Textual model")), } } } Topic::ModelChecks => { const { Article { summary: "Use checks to find invalid structure and unfinished engineering work. A valid model can still need decisions, behavior evidence or allocation.", keywords: "validation warning error completeness coverage non_leaf_work_exchange work_without_behavior_context exchange_without_behavior_context mission_without_capability capability_without_behavior_context unsupported_capability sequence_gap allocation leaf context diagnostics lint", sections: &[ explain( "Errors and warnings answer different questions", "An error rejects the candidate, leaving the accepted model unchanged. A warning describes an incomplete or questionable model that can still be saved. Read the record, field and source location rather than treating the number of warnings as a quality score. Fix structural errors first: the completeness pass waits for valid structure, so its warnings may appear after those errors are corrected.", ), walkthrough( "Connect work to explicit behavior", &[ "For an exchange on non-leaf work, identify the leaf Activity or Function responsible for that endpoint. In SA, keep its typed input/output port owned by that Function. Do not add a fake child just to suppress the warning.", "For leaf work without behavior context, include a work action or one of its exchanges in an ordered Process/Functional chain or Scenario. Allocation alone states who performs the work, not how it is used.", "For an exchange without behavior context, include that exact Interaction or Functional exchange in an exchange occurrence. A similarly named exchange or an unlisted occurrence under the container does not provide evidence.", "Only included steps, fragment operands and valid reused behavior contribute. State actions, availability links and prose remain useful but do not stand in for an explicit process/scenario use. Coverage demonstrates inclusion, not execution or completeness of every possible path.", ], ), walkthrough( "Connect purpose to behavioral evidence", &[ "For a mission without a capability, connect the mission to the capability that delivers it. Merely involving a participant in the mission does not identify that capability.", "For a supported capability without behavior context, connect a Process/Functional chain using Chain involvement or set a Scenario's Context to that capability in the same perspective.", "A capability used only to group child capabilities is treated differently from an isolated leaf. A directly contextualized Scenario establishes support; adding an unrelated participant link is not required merely to clear a warning.", "Review the engineering statement after each correction. Do not invent relationships solely to achieve a warning-free list; leave an unfinished decision visible until the model can express it accurately.", ], ), explain( "Review sequence gaps and incomplete contracts", "A sequence gap calls attention to disconnected handoffs; inspect the contribution endpoints and intended path. Missing allocations, unspecified exchange mechanisms and incomplete conditions need their own engineering decisions. An accepted model does not prove that guards are executable, units are converted, external artifacts exist or timing requirements are met.", ), explain( "The same checks are available to text and automation", "Model checks, source lint and server validation use the shared model rules. Source-project diagnostics identify the included file and location; native JSON checks identify the model record and field. A validation request can inspect a candidate without committing it. Correct errors in the complete candidate before applying it graphically, as text or through the API.", ), ], related: &[ Topic::Capabilities, Topic::Chains, Topic::Scenarios, Topic::Recovery, ], destination: Some((Page::Validation, "Open Model checks")), } } } Topic::Recovery => { const { Article { summary: "Correct rejected drafts, inspect model checks and recover retained work after an unexpected exit. Recovery copies preserve editing separately from your saved models.", keywords: "validation checks error warning invalid alias missing reference undo redo revert recover recovery checkpoint autosave crash draft unsaved structural external conflict lint home", sections: &[ walkthrough( "Resolve a model issue", &[ "Open Model checks and read the issue's message and severity. Use its Open action, where available, to inspect the affected record.", "Correct its properties or missing relationships. Check the active perspective and compatible parent/endpoints before changing record kinds.", "Apply changes and revisit Model checks. A warning about missing allocation or coverage may describe unfinished work; a rejected structural error must be corrected before that candidate can become the model.", ], ), explain( "A property draft is not yet the model", "Apply changes validates the candidate. When it is rejected, correct the retained draft or use Revert. The canvas continues to show accepted values. Navigation that needs to apply an invalid draft may remain on the current record so your work is not lost. You can read this guide without applying or discarding that draft.", ), explain( "Aliases and references", "Stable source aliases must follow the language's identifier rules and avoid reserved words. Use a simple descriptive identifier such as assess_incident. Spaces belong in the displayed Name, not the alias. Reference pickers offer compatible model records; creating the required target first is often the missing step.", ), explain( "Undo and recover deliberately", "Ctrl+Z and redo restore accepted model changes. While the source editor is active, they operate on the source draft instead. Revert restores the accepted source or properties. If a file changed externally, save refuses a blind overwrite: preserve your work with Save as, then compare the files.", ), explain( "Recover an interrupted source save", "If a staged multi-file save was interrupted, Textual model offers Recover interrupted save. Recovery checks the journal and file contents before restoring originals; it does not overwrite unrelated external changes. This repairs an interrupted explicit save. Home's recovery copies preserve unsaved editing separately.", ), walkthrough( "Recover after an unexpected exit", &[ "Open Home and look under Recover unsaved work. Each retained session shows its model name, original location when known and age. Choose Recover; the current model still uses the normal Save/Discard/Cancel guard.", "Inspect the recovered copy. Accepted model/source content and raw source buffers are retained, including invalid and detached drafts. Supported pending property/behavior forms and workflow drafts return with rejected values and semantic-review choices. Correct or revert unfinished input before applying it.", "Choose Save as and select a destination. Recovery does not reconnect the original local or hosted destination, and never restores credentials. Undo/redo history is not restored across sessions.", ], ), explain( "What a checkpoint protects", "While work is unsaved, a private local checkpoint is attempted every 10 seconds. It does not apply drafts, modify model files or save to the server. Recent edits since the last completed checkpoint may not survive a crash. Home reports unavailable or damaged recovery files without replacing the currently open model. Explicit saves and version control remain your durable milestones.", ), explain( "Save work before leaving", "New, Open, opening a recent model, loading the example, Recover and Close use the unsaved-work prompt. Save and continue, discard or cancel is your decision. Checkpoints are private recovery copies; continue saving meaningful milestones and using version control or backups.", ), ], related: &[ Topic::ModelChecks, Topic::Source, Topic::Files, Topic::SemanticReview, ], destination: Some((Page::Validation, "Open Model checks")), } } } Topic::Files => { const { Article { summary: "Keep editable model files and exported pictures distinct. Save the model to retain its semantics, identities and traceability.", keywords: "file home recent missing locate open save save as keyboard shortcuts ctrl control shift export svg json arcadia backup external changes migration", sections: &[ explain( "Start from Home", "Home offers New model, Open model, the rescue example and this guide. Recent models lists local files you successfully opened or saved. A disconnected drive or moved file remains visible; use Locate to choose its new location or Refresh after reconnecting the drive. Remove forgets the recent entry without deleting its model file. Home retains the current document; Continue editing returns to the last workspace, including Textual model or a saved state view, with its drafts intact.", ), explain( "Editable formats", ".arcadia is editable text suitable for Git and can include other source files. .json is the typed model exchange format. Both preserve the persistent model, diagrams and traceability; only a source project preserves its comments and file organization. Export SVG creates a diagram image for reports; it is not an editable model backup.", ), example( "Keyboard reference", "Shortcuts act on the current project. In Textual model, undo and redo affect the source draft.", "Ctrl+N New project\nCtrl+O Open model\nCtrl+S Save all\nCtrl+Shift+S Save as\nCtrl+Z Undo\nCtrl+Shift+Z Redo\nCtrl+Y Redo\nEscape Cancel the active dialog", ), walkthrough( "Save and share a model", &[ "Use Save all to apply valid drafts and save the source project to the current path. For a new project, select a destination and format in the file dialog.", "Use Save as for a new path or explicit format conversion. Check the filename extension when selecting text or JSON.", "After saving, check All changes saved. Share or commit the entry and every included source file, together with the necessary external evidence locations.", "If another tool changed or deleted the open file, preserve your candidate using a new Save as destination. Compare and reconcile the versions before replacing either one.", ], ), explain( "Save as preserves the engineering history", "Saving a source project under a new entry name relocates the save destination without adding a model edit or clearing Redo. Choose a name that does not collide with an included file, including one retained in Undo/Redo history. Save all includes every accepted source file; an unchanged entry file alone does not prove that an included file was saved. If a save fails, the current model and drafts remain available.", ), explain( "Open and migration", "Open validates a complete candidate before replacing the project. Supported earlier versions migrate to the current schema; Model checks shows migration details such as preserved ports, supplied data defaults, generated interface allocations and unresolved message paths. Old Event items become Information notifications; unknown physical mechanisms remain Unspecified. Source upgrades retain files, includes, nested ownership and comments. Opening does not overwrite the original: use Save as if it must remain in its older format. Invalid input leaves the accepted project unchanged.", ), ], related: &[Topic::Source, Topic::Recovery, Topic::Artifacts], destination: None, } } } Topic::Server => { const { Article { summary: "Use optional hosted storage from the desktop, scripts and engineering-tool plugins. All clients work with the same model, validation and authored source.", keywords: "server rest api automation ai plugin curl http etag if-match revision resource_id deploy hosted cad scripting openapi token", sections: &[ example( "Start a local service", "From the repository root, run the command below in a terminal and leave it running. The default address is loopback 127.0.0.1:8787, with platform-local storage. docs/server.md also covers installed executables, authentication and Linux deployment.", "cargo run --locked -p oa-server --bin arcadia-server\n\nGET http://127.0.0.1:8787/health\nGET http://127.0.0.1:8787/api/v1/openapi.json", ), walkthrough( "Open or publish a hosted model", &[ "Choose Hosted models... in the top bar. Enter the service root, such as http://127.0.0.1:8787, without /api/v1. Enter the configured bearer token if required, then Connect / refresh.", "Choose Publish as new to upload the current source project, including its files and comments, as a separate hosted resource. Its acknowledgement switches the current Save destination to that resource.", "To open an existing resource, search the Hosted model catalog, select a model and choose Open selected. More models loads another page. Unsaved work uses the normal save/discard/cancel prompt.", "Edit diagrams, properties or Textual model, then choose Save to server or press Ctrl+S. Valid drafts are applied; invalid drafts remain available to correct or revert. Edits and Undo stay local until Save.", ], ), walkthrough( "Review another client's changes", &[ "Check server reads the current revision without replacing local content. A stale Save returns 412 and automatically checks the server for review; local work is retained.", "Review the changed file list. View server text opens a read-only file; Back to review returns to the list. There is no automatic merge.", "Reload server version adopts the reviewed source project as one undoable local change after checking unapplied drafts. Undo restores the previous accepted local version without changing the server. Reconcile your intended changes before saving again.", "After an unknown write outcome, use Check previous write when offered. If publication may already have succeeded, inspect the refreshed catalog before publishing another copy.", ], ), explain( "Return to a local file", "Choose Save as local... or Ctrl+Shift+S, then save an .arcadia entry to retain included files and comments. A successful source-project save makes that file the current destination and leaves the hosted resource unchanged. JSON exports the model only and retains the hosted destination and unsaved state. Canceling or a failed save also retains the hosted destination. Forget connection becomes available after leaving the hosted session.", ), explain( "Connection credentials", "The desktop keeps credentials in memory only. Use HTTPS remotely; HTTP is allowed only for localhost or a loopback IP. Put the token in its own field, never the service URL. URLs with credentials, queries or fragments are rejected, and redirects are not followed. To update a token, enter it and Connect / refresh for the same service, then retry the check or Save.", ), walkthrough( "Automate model edits", &[ "Create or import a project using /api/v1/projects or /api/v1/projects/import. Keep the returned service resource_id; it is distinct from the editable model UUID.", "Read the project's envelope, source or model. Preserve its exact ETag as the precondition for your edit.", "Submit typed commands to /transactions, or a complete candidate to /source, /model or /source-project, using If-Match. Use transaction dry-run or validation before committing substantial generated changes.", "A stale write returns 412. Keep the user's draft, reload the current head and reconcile deliberately. After an uncertain write outcome, read the server state before retrying.", "Query /artifact-links and /traces to connect domain-tool artifacts to model records. The server stores these references; the integration is responsible for resolving its external artifacts.", ], ), explain( "Validate and save a source bundle", "GET /source-project returns the entry and included files. POST /validation with format source-project and source_project containing that bundle checks a candidate without committing it; warnings can coexist with valid: true. PUT /source-project accepts the complete bundle under the current If-Match ETag. Structural errors reject the write, preserving the accepted model and revision. Compare record IDs and fields across text/JSON diagnostics; source-project results also identify the included file and source span.", ), explain( "Deployment and persistence", "Non-loopback binding requires a configured bearer token. Use the documented local-disk storage and deployment configuration. The service keeps the current revision and serializes accepted updates; it does not provide retained revision history or server-side undo. Stop the service cleanly before copying its committed store for backup.", ), explain( "Use the precise contract", "docs/api.rst and docs/openapi.json define routes, command shapes, limits and errors. The running server also serves OpenAPI. No network request or service mutation is triggered by reading this guide.", ), ], related: &[ Topic::Artifacts, Topic::Source, Topic::Transitions, Topic::Recovery, ], destination: None, } } } } } /// Help search is independent of model search. All query words must occur in /// the article, title or retrieval keywords; order and case are insignificant. fn search(query: &str) -> Vec { let query = query.to_lowercase(); let words = query.split_whitespace().collect::>(); Topic::ALL .into_iter() .filter(|topic| { let article = article(*topic); let mut haystack = format!("{} {} {}", topic.title(), article.summary, article.keywords); for section in article.sections { haystack.push_str(section.title); haystack.push(' '); haystack.push_str(section.body); for step in section.steps { haystack.push(' '); haystack.push_str(step); } if let Some(code) = section.code { haystack.push(' '); haystack.push_str(code); } } let haystack = haystack.to_lowercase(); words.iter().all(|word| haystack.contains(word)) }) .collect() } fn topic_button(topic: Topic, selected: bool) -> iced::widget::Button<'static, Message> { button(text(topic.title()).size(13)) .padding([10, 11]) .width(Fill) .style(if selected { style::selected } else { style::nav }) .on_press(Message::Guide(Event::Topic(topic))) } fn article_view(topic: Topic) -> Element<'static, Message> { let article = article(topic); let mut content = column![ text(topic.category()).size(11).color(style::MUTED), text(topic.title()).size(25), text(article.summary).size(15).color(style::MUTED), ] .spacing(14) .width(Fill); for section in article.sections { let mut body = column![text(section.title).size(18)] .spacing(12) .width(Fill); if !section.body.is_empty() { body = body.push(text(section.body).size(14)); } for (index, step) in section.steps.iter().enumerate() { body = body.push( row![ text(format!("{:02}", index + 1)) .size(13) .color(style::TEAL) .width(28), text(*step).size(14).width(Fill), ] .spacing(9) .width(Fill), ); } if let Some(code) = section.code { body = body.push( container(text(code).font(Font::MONOSPACE).size(12).width(Fill)) .padding(14) .width(Fill) .style(style::subtle), ); } content = content.push(container(body).padding(20).width(Fill).style(style::card)); } if let Some((page, label)) = article.destination { content = content.push( button(text(label).size(13)) .padding([11, 15]) .on_press(Message::Page(page)), ); } if topic == Topic::FirstProject { content = content.push(text("For a worked model, open the water-pressure walkthrough. Copy the whole examples/release1 folder and open the copied project.arcadia before editing; read its README.md. If you use Save as, also copy evidence/ beside the new entry: Save as copies model source, not external artifact targets.").size(13)); content = content.push( button(text("Open water-pressure walkthrough").size(13)) .padding([11, 15]) .style(style::nav) .on_press(Message::Home(crate::home::Event::Walkthrough)), ); content = content.push( button(text("Open coastal rescue example").size(13)) .padding([11, 15]) .style(style::nav) .on_press(Message::Request(Pending::Demo)), ); } content = content.push(text("CONTINUE EXPLORING").size(11).color(style::MUTED)); for related in article.related { content = content.push(topic_button(*related, false)); } scrollable(content.padding(iced::Padding { right: 12., bottom: 24., ..Default::default() })) .id(ARTICLE_SCROLL_ID) .width(Fill) .height(Fill) .into() } pub fn view(state: &State) -> Element<'_, Message> { let matches = search(&state.query); let mut navigation = column![].spacing(5).width(Fill); let mut category = ""; for topic in &matches { if category != topic.category() { category = topic.category(); navigation = navigation.push( container(text(category).size(10).color(style::MUTED)).padding(iced::Padding { top: 12., bottom: 4., left: 8., right: 4., }), ); } navigation = navigation.push(topic_button(*topic, state.topic == *topic)); } let topic_list = container(scrollable(navigation.padding(iced::Padding { right: 8., ..Default::default() }))) .width(200) .height(Fill); let article: Element<'_, Message> = if matches.is_empty() { container(column![ text("No help topics found").size(23), text("Try a modeling concept such as allocation, scenario, data type, Git or API. Help search does not search your model.").size(14).color(style::MUTED), button(text("Clear search").size(13)).padding([10, 14]).on_press(Message::Guide(Event::Search(String::new()))), ].spacing(16).max_width(520)).padding(24).width(Fill).into() } else { // Topic changes also issue an explicit article-only scroll reset. keyed_column([(state.topic, article_view(state.topic))]) .width(Fill) .height(Fill) .into() }; column![ column![ text("Engineering guide").size(25), text("Practical workflows, modeling concepts and reference · available offline") .size(12) .color(style::MUTED), ] .spacing(5), row![ text_input("Search help: allocation, scenario, Git…", &state.query) .on_input(|query| Message::Guide(Event::Search(query))) .padding(11) .size(14) .width(Fill), button(text("Clear").size(12)) .padding(11) .style(style::nav) .on_press_maybe( (!state.query.is_empty()).then(|| Message::Guide(Event::Search(String::new()))) ), ] .spacing(8) .width(Fill), text(format!("{} topics", matches.len())) .size(11) .color(style::MUTED), row![topic_list, article] .spacing(20) .width(Fill) .height(Fill), ] .spacing(14) .padding(22) .width(Fill) .height(Fill) .into() } #[cfg(test)] mod tests { use super::*; #[derive(Default)] struct ScrollProbe { snaps: Vec>>, } impl iced::advanced::widget::operation::Scrollable for ScrollProbe { fn snap_to( &mut self, offset: iced::advanced::widget::operation::scrollable::RelativeOffset>, ) { self.snaps.push(offset); } fn scroll_to( &mut self, _: iced::advanced::widget::operation::scrollable::AbsoluteOffset>, ) { panic!("Guide navigation must snap to the article start"); } fn scroll_by( &mut self, _: iced::advanced::widget::operation::scrollable::AbsoluteOffset, _: iced::Rectangle, _: iced::Rectangle, ) { panic!("Guide navigation must not use a relative pixel scroll"); } } #[tokio::test] async fn guide_topic_navigation_resets_only_article_and_same_topic_search_keeps_scroll() { use iced::futures::StreamExt; let mut app = crate::app::App::with_project(oa_core::Project::empty()); app.page = Page::Guide; let accepted = app.document.snapshot(); let revision = app.document.revision(); for (event, topic, reset) in [ (Event::Topic(Topic::Scenarios), Topic::Scenarios, true), ( Event::Search("sender lifeline".into()), Topic::Scenarios, false, ), // Native reproduction: related topic after scrolling Scenarios. (Event::Topic(Topic::Exchanges), Topic::Exchanges, true), (Event::Search("If-Match 412".into()), Topic::Server, true), (Event::Search("If-Match".into()), Topic::Server, false), ( Event::Search("nonexistent-topic-987".into()), Topic::Server, false, ), (Event::Search(String::new()), Topic::Server, false), (Event::Topic(Topic::Server), Topic::Server, false), ] { let mut article = ScrollProbe::default(); let mut navigation = ScrollProbe::default(); if let Some(mut stream) = iced_runtime::task::into_stream(app.update(Message::Guide(event))) { while let Some(action) = stream.next().await { let iced_runtime::Action::Widget(mut operation) = action else { panic!("Guide navigation must not schedule model or external actions"); }; for (id, probe) in [ (ARTICLE_SCROLL_ID, &mut article), ("guide-topic-navigation", &mut navigation), ] { operation.scrollable( Some(&iced::advanced::widget::Id::new(id)), iced::Rectangle::default(), iced::Rectangle::default(), iced::Vector::new(0., 900.), probe, ); } } } assert_eq!(app.guide.topic, topic); assert_eq!(article.snaps.len(), usize::from(reset)); for offset in article.snaps { assert_eq!(offset.x, Some(0.)); assert_eq!(offset.y, Some(0.)); } assert!(navigation.snaps.is_empty()); assert_eq!(app.project(), &accepted.decoded.project); assert_eq!(app.document.bundle(), &accepted.bundle); assert_eq!(app.document.revision(), revision); assert!(!app.document.can_undo()); } } #[test] fn search_finds_concepts_in_walkthroughs_and_api_reference() { assert!(search(" ALLOCATED rescue ").contains(&Topic::FirstProject)); assert!(search("sender lifeline").contains(&Topic::Scenarios)); assert!(search("If-Match 412").contains(&Topic::Server)); assert!(search("Ctrl+Shift+S").contains(&Topic::Files)); assert!(search("no-such-modeling-topic-987").is_empty()); assert_eq!(search(" \n\t "), Topic::ALL); } #[test] fn filtering_and_related_navigation_keep_selection_visible() { let mut state = State::default(); state.update(Event::Search("If-Match 412".into())); assert_eq!(state.topic, Topic::Server); state.update(Event::Topic(Topic::Nesting)); assert!(state.query.is_empty()); assert_eq!(state.topic, Topic::Nesting); state.update(Event::Search("nonexistent-topic-987".into())); assert_eq!(state.topic, Topic::Nesting); state.update(Event::Search(String::new())); assert!(search(&state.query).contains(&state.topic)); } }