Compare commits

...
Author SHA1 Message Date
sorokin.oleg.edu 4b9932236a Sync agent instructions from main 2026-09-13 11:40:17 +03:00
sorokin.oleg.edu afd81af1f1 Sync agent instructions from main 2026-09-12 13:43:57 +03:00
sorokin.oleg.edu 35d9eab302 Sync agent instructions from main 2026-09-12 13:40:39 +03:00
sorokin.oleg.edu 22e974da35 Sync agent instructions from main 2026-09-12 13:16:22 +03:00
sorokin.oleg.edu bf3f2f69a2 Sync agent instructions from main 2026-09-11 19:52:31 +03:00
sorokin.oleg.edu 75b8815a83 Sync agent instructions from main 2026-09-11 15:55:57 +03:00
sorokin.oleg.edu b2127cbe90 Sync agent instructions from main 2026-09-11 14:34:52 +03:00
sorokin.oleg.edu 1bf8ebca96 Record combined DATA scenario verification and frozen Windows artifact
Rust checks / check (windows-latest) (push) Canceled after 0s
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
2026-09-06 05:04:32 +03:00
sorokin.oleg.edu dfd2e41259 Merge tested R6 data workflows into direct scenario workspace 2026-09-06 05:04:04 +03:00
sorokin.oleg.edu 05a84b217d Record final scenario save-scroll review and build evidence 2026-09-06 04:59:01 +03:00
sorokin.oleg.edu e7e37d485d Keep observed scenario scrolling current during async saves 2026-09-06 04:58:45 +03:00
sorokin.oleg.edu b35dede75f Browse message value properties by record with bounded pages 2026-09-06 04:56:41 +03:00
sorokin.oleg.edu 5fa9c905c8 Record verified scenario review fixes and fresh binary evidence 2026-09-06 04:54:22 +03:00
sorokin.oleg.edu 7a666076bf Fix scenario scroll cancellation, operand activations and picker cost 2026-09-06 04:53:23 +03:00
sorokin.oleg.edu f5467e50c5 Preserve authored source and reports through desktop migration 2026-09-06 04:43:41 +03:00
sorokin.oleg.edu 74989b6f25 Keep nested data field names visible in compact pickers 2026-09-06 04:40:08 +03:00
sorokin.oleg.edu 524dabb9c3 Record direct scenario checks and pending native acceptance 2026-09-06 04:33:36 +03:00
sorokin.oleg.edu d1d8f0bbed Add direct scenario gestures and atomic ordered message insertion 2026-09-06 04:33:13 +03:00
sorokin.oleg.edu 0edadeebdb Record R6 cross-platform tests and native data workflows 2026-09-06 04:29:29 +03:00
sorokin.oleg.edu 93e3137342 Plan direct scenario drawing and ordered insertion milestone 2026-09-06 04:14:22 +03:00
13 changed files with 3945 additions and 335 deletions
+73
View File
@@ -0,0 +1,73 @@
# Agent working agreement
## Working method
- Follow `PROJECT_GOAL.md` and the user's current instructions. Work autonomously within scope. Resolve discoverable questions yourself; record, test, and revise reversible assumptions. Ask only when a consequential decision needs information or authorization not already available. Continue independent useful work while blocked elsewhere.
- Map unfamiliar domains through primary sources, comparable systems, and real user workflows. Identify underlying needs, valuable concepts, and avoidable friction; evaluate improvements rather than merely reproducing features. Synthesize findings into decisions or experiments, and stop research when another action offers greater value.
- Make specifications precise enough for compatible independent implementations: definitions, relationships, invariants, valid/invalid examples, and lifecycle or transformation rules where relevant. Trace important requirements and decisions to implementation and verification. Resolve specification ambiguity at its source when implementation exposes it.
- For consequential uncertain choices, compare materially different candidates against explicit user-derived criteria. Ask independent critics to challenge assumptions and attempt to falsify proposals; refine or replace candidates as evidence warrants. Record the choice, evidence, tradeoffs, rejected approaches, and reasons to revisit it. Use proportionate effort for reversible local choices.
- Plan milestones that resolve uncertainty or deliver useful capability, with explicit dependencies. Test foundational assumptions before substantial downstream work. At checkpoints, assess progress toward the actual objective and replan when evidence invalidates the strategy or progress stalls.
- Define observable success and suitable evaluators before repeated improvement: invariants, examples, round trips, transformations, performance checks, realistic user tasks, or explicit review rubrics. Ensure the evaluator measures the intended outcome. For experiments, record the question, expectations, method, result, and implications; isolate exploratory prototypes from production architecture.
- Build and run meaningful changes, exercise actual behavior, and inspect useful logs. Investigate crashes, serious warnings, corrupted state, and interaction defects. For GUI work, use Computer Use or equivalent tools to inspect realistic workflows, cognitive load, editing/selection, navigation, feedback, recovery, loading/empty/failure states, accessibility, layout, focus/disabled states, and responsiveness.
- Match independent reviews to the artifact and risks. For languages or formats, include ambiguity, round trips, human/AI editability, and version-control behavior where relevant. Verify findings, fix the appropriate conceptual, specification, architectural, implementation, or interaction layer, and recheck affected dependents. Reuse valid evidence; repeat checks for concrete risks, changes, findings, or required gates. Compilation, screenshots, and worker reports alone do not establish correctness or usability.
- Favor the simplest maintainable design satisfying known requirements and justified extensibility. Avoid speculative abstraction, unrelated refactoring, and repeating approaches already invalidated by evidence.
## User input
All content between `BEGIN USER-OWNED APPLICATION SPECIFICATION` and `END USER-OWNED APPLICATION SPECIFICATION` in `PROJECT_GOAL.md` belongs to the user. Its headings and structure are unrestricted. Preserve it exactly unless the user explicitly requests an edit; cleanup and task completion are not permission to rewrite it.
Derive specifications, plans, and acceptance checks elsewhere, linking to their source. Distinguish user requirements, verified facts, assumptions, open questions, candidate approaches, and accepted decisions. Interpretations must not silently replace the user's intent. When input changes, reconcile affected work. Check current evidence before reopening a reported problem merely because its original description remains in the input.
## Shared records
Keep one authoritative home for each kind of information. Use links and brief scoped summaries elsewhere; update only what materially changed. Local planning tools and scratch notes need no routine synchronization.
| Information | Authoritative home |
| --- | --- |
| Live plans, priorities, dependencies, ownership, status, blockers, and handoffs | Gitea issues/milestones; linked PRs hold change review and validation. |
| Specifications, architecture, interfaces, and build/run documentation | Established version-controlled project documents. |
| Durable decisions and reusable evidence | `harness/DECISIONS.md` and relevant topic notes, or existing equivalents. |
| Navigation and integrated-state checkpoint | `harness/STATE.md`: links, verified code commit/release, capabilities, and material limitations. No live task ledger. |
| Shared entrypoint | Gitea Wiki: overview and links, without duplicating versioned documentation. |
| Published deliverables | Gitea releases, tied to source commits and verification. |
Use Gitea for live coordination and the relevant code version for implementation facts. Required behavior comes from the user and supporting specifications; observed behavior and issue closure do not redefine requirements.
## Coordination and integration
All independent agents coordinating through Gitea run on completely separate machines, each with its own clone, filesystem, tools, processes, and runtime. Gitea and pushed Git history are the shared communication and delivery channels. Local files (including `harness`), unpushed commits, worktrees, test results, and localhost services are not accessible to peers. Matching branch names or paths do not imply synchronized contents.
1. On starting or resuming, including after compaction, inspect the working branch, local changes, applicable instructions, relevant shared records, and existing implementation. Recover context from authoritative records. Check for newer shared instructions on the actual remote default branch without overwriting local work. Read what is relevant; avoid repeated full-history scans and constant polling.
2. Search before creating tasks. Claim a meaningful outcome in its Gitea issue with a stable session identity, scope, remote repository/branch, and acceptance criteria. Keep small steps within that task. Recheck after claiming; comments are not atomic locks. Respect existing ownership and resolve overlapping claims before overlapping work.
3. Independent sessions are peers. Each owns its contribution and delegated work on its own machine. Record one integration/release owner when a milestone needs shared coordination. Use distinct remote work branches, partition files/interfaces, and coordinate changes to shared specifications and documents. Worktrees provide local isolation only; they do not share work between machines.
4. At meaningful checkpoints or handoffs, record changed results, commit/PR links, verification commands and environment, blockers, and next actions. Identify the tested code version and label uncommitted or unpublished work as local-only. Before asking peers to consume results, push the required code and versioned documentation, including relevant `harness` updates; publish other required artifacts in Gitea and link them. Use repository-relative paths with commit references, reproducible commands, and necessary environment details; local paths and localhost URLs alone are not handoffs. Do not assume another agent is running or has read a message; silence alone does not establish abandonment. Record evidence and preserve existing work when reassigning a task.
5. Follow project review and merge conventions. Fetch the relevant remote branches and inspect the identified commits before reviewing or integrating another machine's work. Resolve conflicts while preserving others' edits, and verify the combined result locally; report material environment differences. Local instruction-file edits reach peers only after publication and retrieval. Do not overwrite others' work or force-push shared history to resolve coordination problems.
6. Close work when its acceptance criteria are met and the result is integrated or delivered as appropriate. Mark duplicates and superseded tasks explicitly with replacement links. Then choose further useful unowned work while the requested objective remains incomplete.
Use Gitea MCP for shared coordination. If unavailable, report the failure, buffer necessary handoff information locally, and continue already-owned or clearly non-overlapping work. Reconcile with current Gitea state when access returns; do not claim an ownership update, review, or publication succeeded without confirmation.
## Delegation and reasoning
Use GPT-6 Astra exclusively. Gitea peers are independent remote sessions, not subagents controlled by this session. Spawned subagents use the execution environment actually provided by the agent tools; do not assume they share files, and publish their results before Gitea peers need them. Delegate bounded independent research, design, implementation, or review when it improves progress, judgment, or preserves the parent's context. Give subagents the relevant context, ownership boundaries, result format, and evidence requirements. Use fresh critics when practical. The parent owns synthesis and integration and normally consolidates results into Gitea. Choose concurrency according to useful independent work and available resources, including other sessions; there is no agent-count quota.
Optimize reasoning effort for verified outcomes per cost, including review, retries, and repair:
- Main agent: default to high for planning, synthesis, integration, and acceptance decisions; medium for dispatching a settled plan and routine coordination.
- Subagents: default to medium for bounded implementation and targeted research; low for straightforward retrieval, mechanical edits, and running established checks; high for complex debugging, correctness review, adversarial analysis, or ambiguous design.
- Use extra-high selectively for difficult foundational semantic, methodological, formal, or cross-domain decisions. Reserve max for exceptional unresolved problems where additional reasoning is likely to justify its cost. Retain higher settings when evidence supports their value.
Allocate by task difficulty and consequences, not role or length alone. Start higher when a clearly difficult decision has expensive downstream consequences. Escalate for concrete reasoning difficulties; resolve missing evidence or unclear requirements at their source. Set subagent effort explicitly through supported controls to avoid unintended inheritance. Do not claim an unapplied change or alter global configuration merely to enforce these preferences.
## Memory and maintenance
Preserve concise records of decisions and rationale, useful experiments, failed approaches, validation evidence, and remaining work. Link substantial evidence rather than copying logs into indexes. Update the integrated checkpoint after meaningful integration; mark branch-specific evidence as such.
Consolidate duplicates, repair stale guidance, and remove obsolete temporary files alongside useful project work. Before removing material, check references, active ownership, and ongoing use; preserve unique knowledge and update affected links. Prefer closing, superseding, or archiving history over deletion. User input is excluded from autonomous cleanup.
If legacy local task ledgers or plans duplicate Gitea, migrate their live content once into existing or appropriately scoped issues, preserve useful decisions/evidence, and mark the old records superseded. Record the migration so later sessions do not repeat it. Do not create empty documents or maintain records for ceremony.
## Delivery
Publish versioned Gitea releases at meaningful working milestones; mark unfinished versions as prereleases. Check existing releases/tags and coordinate one owner and version before publishing. Build from an identified integrated commit and verify the packaged result and launch steps.
Include runnable builds or appropriate artifacts, concise release notes, build/run/deployment instructions as applicable, known limitations, tested platforms, and verification evidence. Confirm publication and artifact availability. Use existing authorized API access or project tooling when MCP lacks a needed release/upload operation. Do not silently replace published versions with different contents.
+111
View File
@@ -0,0 +1,111 @@
# Project goal
## Objective
Take the existing project to the complete, coherent, usable, and verified outcome defined by the user input below. Solve the underlying problem end-to-end, preserving useful work already completed.
Treat domain understanding, research, methodology, semantics, specification, product design, UX, architecture, experimentation, implementation, and validation as first-class work. Choose what most advances the objective rather than following a predetermined development sequence.
Follow `AGENTS.md` for execution, coordination, record ownership, and delivery. Continue until the completion criteria are met; plans, prototypes, successful builds, and individual milestones are intermediate results. Do not silently weaken requirements or completion standards to finish sooner.
## Completion criteria
Apply these criteria to the scope requested by the user:
- Required capabilities address the intended problem and support realistic user workflows.
- Important concepts, semantics, and interfaces are precise and consistent; specifications agree with implemented behavior.
- Consequential decisions have evidence and rationale. Major assumptions have been validated, explicitly accepted, or isolated without compromising required behavior.
- The result builds, runs, and operates on the required platforms. Relevant tests and evaluators pass; integrations, edge cases, and failure states have been exercised against the actual result.
- User-facing behavior and usability have been exercised directly, using representative data and workflows.
- A fresh independent review covers requirements, domain/semantic consistency, usability, architecture, correctness, regressions, runtime behavior, documentation, and remaining risks. Valid material findings are addressed, blocking and high-impact issues resolved, and affected behavior reverified; no known blocker or high-severity defect remains.
- Deliverables are maintainable, appropriately extensible, documented, and published as required by `AGENTS.md`. Obsolete scaffolding is removed; documentation and shared state accurately describe the result. Another engineer or agent can reproduce the verification and continue the work.
If required work or verification is blocked, record the specific gap and continue independent useful work without claiming completion.
## Final handoff
Link the delivered result and summarize capabilities, discoveries, architecture and decisions, relevant rejected approaches, verification and independent review, remaining limitations, and exact build/run/deployment/verification instructions. Refer to authoritative records instead of repeating the project history.
## USER-OWNED APPLICATION SPECIFICATION
The block below accepts all project-specific requirements, context, priorities, and feedback, with any headings or organization the user chooses.
<!-- BEGIN USER-OWNED APPLICATION SPECIFICATION -->
## Application specification
### General description
Create a modern clone of ARCADIA Capella using Rust Iced framework. It should be cross platform (supporting Windows and Linux at least). Use Rust exclusively (wherever possible & practical).
Focus on making the application modern, extensible, intuitive and easy to use. When writing code and making software architecture decisions focus on long term maintainability, extensibility while making the project friendly for active development
Try to address all known problems / downsides of the original ARCADIA Capella software.
Version 1 MVP in the form of working Operational Analysis layer is done.
Version 2 MVP in the form of working Operation Analysis + System Analysis is done.
You should make the 1st release version of the project with fully feature complete Operation Analysis + System Architecture, according to this application specification.
Remaining ARCADIA layers will be added in future releases.
Transitioning of model elements between layers (in both directions) should be easy and intuitive.
Keep in mind that the target audience for the application are system engineers. And you should satisfy their needs and use cases
The application should be a general systems modeling tool, like ARCADIA Capella or SysML v2
You should implement most of the ARCADIA semantics, while adressing original ARCADIA Capella problems / downsides
The original ARCADIA Capella UI is very bloated, clunky, unintuitive and hard to use. In addition, it lacks a number of quality of life features. You should at all cost avoid making the same mistakes as the developers of the original ARCADIA Capella
You should explore the functionality & UI of the original ARCADIA Capella to take the most useful and powerful ideas from it. While making them modern, easy to use and understand
Original ARCADIA Capella uses "nested" elements (visually nested) to signify various relationships, such as "Containment relationship" or "Deployment relationship". Instead of using visually noisy arrows / graph edges. I pretty much like this approach and want to see something similar to it in this application
To explore the original ARCADIA Capella UI & semantics you may use web resources / web search / official and community docs. Or open the original ARCADIA Capella installed on this desktop machine for exploration
Your second, parallel, goal is to create a textual representation for ARCADIA, similar to SysML v2. So a user can either edit a system model using UI (graphical representation) or text. Both graphical and textual representations should be fully equivalent and have full parity. You may take inspiration from SysML v2. Both graphical and textual representations should have one overarching, concrete, unambiguous spec in reST format, similar to SysML v2 strictness
The application should support both forms of editing
The textual representation should be easily editable and understandble for both humans and AI
The textual representation should be easily handled by version control tools such as Git
The underlying semantics of the textual / graphical representation should support traceability of various development artifacts (for example - code, CAD files, electrical schematics, Docx files and anything in between)
The textual representation editor should have syntax highlighting and linting
The project should also have a backend server / service that you can deploy. The backend server purpose is to give programmatic access to stored system models. So, there also should be a clear REST API for querying, editing and working with models. The purpose of the REST API is to allow automation, scripting and AI-driven workflows
An example could be - writing custom plugins for various domain specific software (version control software, CAD software etc...) to establish traceability between development artifacts and a system model
The desktop application should contain built-in guide / help / tutorial with clear and concise info on modeling concepts, underlying semantics, common modeling scenarios and application features / how to use the app. It should be easy to navigate and explore
@Computer
### Semantic model / metamodel
Requirements:
- Textual notation.
- Graphical notation.
- Constraints and calculations.
- Quantities and units.
- State and behavior modeling.
- Reusable model libraries.
- Strong, standardized specification.
- Strong, standardized API.
#### Textual notation
Requirements:
- Should not have visual noise and clutter.
- Should have means to reduce cognitive load when developing system models.
- Should be easily editable by humans. And AI agents.
- System engineers should be able to easily develop system models (both big and small) using textual notation only.
### UI
The focus should be on reducing system engineer's cognitive load when creating / working with a system model. Regardless of the model size (big or small).
Long, homogeneous lists in the UI that require user to scroll far or search for a desired option for too long - is a bad design.
## Active issues / current guidance
Right now, the behavior & scenarios UI is hard to use. It contains static / uneditable diagrams, which is highly dubious. In addition, user workflow with the behavior & scenarios UI differ significantly from the other UIs - you can't edit diagrams and instead suppose to click on menus for model development
---
The current project state is nowhere near the usability of the original ARCADIA Capella (despite its downfalls) or its functionality / semantics. You should focus on that by studying original ARCADIA Capella very closely and implementing its features in a modern, ergonomic and easy to use way
<!-- END USER-OWNED APPLICATION SPECIFICATION -->
+452 -5
View File
@@ -12,6 +12,216 @@ use oa_core::{
use serde_json::{Value, json};
use std::{collections::BTreeSet, path::PathBuf, sync::Arc};
#[cfg(test)]
mod direct_scenario_tests {
use super::*;
use crate::behavior::{
self,
scenario::{self, CanvasEvent, Event, MessageStyle, Slot},
};
fn setup() -> (App, scenario::direct_tests::Fixture) {
let f = scenario::direct_tests::fixture();
let mut app = App::with_project(f.project.clone());
let _ = app.update(Message::Select(f.owner));
let _ = app.update(Message::Page(Page::Behavior));
assert_eq!(app.scenario_context().unwrap().scenario, f.owner);
(app, f)
}
fn form(app: &mut App, event: Event) {
let _ = app.update(Message::Behavior(behavior::Event::Scenario(event)));
}
fn begin(app: &App, f: &scenario::direct_tests::Fixture) -> CanvasEvent {
CanvasEvent::Begin {
context: app.scenario_context().unwrap(),
slot: Slot {
parent: f.owner,
before: None,
},
sender: f.sender,
receiver: f.receiver,
style: MessageStyle::Asynchronous,
}
}
#[test]
fn direct_scenario_scroll_and_real_resize_invalidate_only_pointer_context() {
let (mut app, f) = setup();
let original = app.document.bundle().clone();
let event = begin(&app, &f);
let context = app.scenario_context().unwrap();
let size = app.window_size;
let _ = app.update(Message::WindowResized(size));
assert_eq!(app.scenario_context(), Some(context));
let _ = app.update(Message::WindowResized(iced::Size::new(
size.width + 20.,
size.height,
)));
assert_ne!(app.scenario_context(), Some(context));
let _ = app.update(Message::ScenarioCanvas(event));
assert!(!app.behavior.has_pending());
let context = app.scenario_context().unwrap();
for (area, offset) in [
(scenario::ScrollArea::Canvas, iced::Vector::new(30., 150.)),
(scenario::ScrollArea::Page, iced::Vector::new(0., 90.)),
] {
let before = app.scenario_context().unwrap();
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
context, area, offset,
)));
assert_ne!(app.scenario_context(), Some(before));
let current = app.scenario_context().unwrap();
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
context, area, offset,
)));
assert_eq!(
app.scenario_context(),
Some(current),
"unchanged redraw notification retains context"
);
}
let event = begin(&app, &f);
let _ = app.update(Message::ScenarioCanvas(event));
form(&mut app, Event::Name("Keep my draft".into()));
let context = app.scenario_context().unwrap();
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
context,
scenario::ScrollArea::Page,
iced::Vector::new(0., 200.),
)));
assert!(app.behavior.has_pending());
assert_eq!(app.document.bundle(), &original);
let current = app.scenario_context().unwrap();
for dimension in 0..3 {
let mut stale = current;
match dimension {
0 => stale.document = Id::new_v4(),
1 => stale.scenario = Id::new_v4(),
_ => stale.revision += 1,
}
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
stale,
scenario::ScrollArea::Canvas,
iced::Vector::new(0., 900.),
)));
assert_eq!(app.scenario_context(), Some(current));
}
assert!(!app.document.can_undo());
}
#[test]
fn direct_scenario_rejects_every_stale_context_and_preserves_property_drafts() {
let (mut app, f) = setup();
let before = app.document.snapshot();
for dimension in 0..4 {
let mut event = begin(&app, &f);
let CanvasEvent::Begin { context, .. } = &mut event else {
unreachable!()
};
match dimension {
0 => context.document = Id::new_v4(),
1 => context.scenario = Id::new_v4(),
2 => context.revision += 1,
_ => context.epoch += 1,
}
let _ = app.update(Message::ScenarioCanvas(event));
assert!(!app.behavior.has_pending());
assert!(Arc::ptr_eq(&before, &app.document.snapshot()));
}
let _ = app.update(Message::Field(
"/name".into(),
"Pending scenario name".into(),
));
assert!(app.draft_dirty());
let event = begin(&app, &f);
let _ = app.update(Message::ScenarioCanvas(event));
assert!(!app.behavior.has_pending());
assert!(app.draft_dirty());
assert!(Arc::ptr_eq(&before, &app.document.snapshot()));
}
#[test]
fn direct_scenario_creation_and_reorder_preserve_selection_and_each_undo_once() {
let (mut app, f) = setup();
let original = app.document.bundle().clone();
let event = begin(&app, &f);
let _ = app.update(Message::ScenarioCanvas(event));
form(
&mut app,
Event::Exchange(Choice {
id: f.exchange,
label: "Contract".into(),
}),
);
form(&mut app, Event::Submit);
assert!(!app.behavior.has_pending());
let created = app.selected;
assert_ne!(created, f.owner);
assert_eq!(
app.project().elements[&f.owner].steps().unwrap(),
&[f.call, f.reply, created]
);
let before_reorder = app.document.bundle().clone();
let context = app.scenario_context().unwrap();
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Select(
context, f.sender,
)));
assert_eq!(
app.scenario_context(),
Some(context),
"same-owner selection retains gesture context"
);
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Reorder {
context,
occurrence: created,
slot: Slot {
parent: f.owner,
before: Some(f.call),
},
}));
assert_eq!(app.selected, created);
assert_eq!(
app.project().elements[&f.owner].steps().unwrap(),
&[created, f.call, f.reply]
);
let _ = app.update(Message::Undo);
assert_eq!(app.document.bundle(), &before_reorder);
let _ = app.update(Message::Undo);
assert_eq!(app.document.bundle(), &original);
assert_eq!(app.project(), &f.project);
assert!(!app.document.can_undo());
}
#[test]
fn direct_scenario_source_failure_and_navigation_keep_form_until_success() {
let (mut app, f) = setup();
let bundle = app.document.bundle().clone();
let mut limits = oa_core::source_project::ProjectLimits::default();
limits.model.max_source_bytes =
bundle.files.values().map(String::len).sum::<usize>() + 4000;
app.document = Document::from_bundle(bundle.clone(), limits).unwrap();
app.sync();
let event = begin(&app, &f);
let _ = app.update(Message::ScenarioCanvas(event));
form(
&mut app,
Event::Exchange(Choice {
id: f.exchange,
label: "Contract".into(),
}),
);
form(&mut app, Event::Name("Long message".repeat(1024)));
let _ = app.update(Message::Page(Page::Catalog));
assert_eq!(app.page, Page::Behavior);
form(&mut app, Event::Submit);
assert!(app.behavior.has_pending());
assert_eq!(app.document.bundle(), &bundle);
assert!(!app.document.can_undo());
form(&mut app, Event::Name("Recovered message".into()));
form(&mut app, Event::Submit);
assert!(!app.behavior.has_pending());
assert_eq!(app.project().named(app.selected), "Recovered message");
let _ = app.update(Message::Undo);
assert_eq!(app.document.bundle(), &bundle);
assert!(!app.document.can_undo());
}
}
#[cfg(test)]
mod behavior_lifecycle_tests;
mod state_diagram;
@@ -130,6 +340,7 @@ pub enum Message {
FieldShape(String, Value),
FieldAppend(String, Value),
FieldValuePath(String, crate::data_values::FieldPathKey),
FieldValueBrowse(String, Option<crate::data_values::FieldPathKey>, usize),
FieldVariant(String, Value),
FieldArray(String, Option<usize>, i32),
Apply,
@@ -147,6 +358,7 @@ pub enum Message {
BehaviorOwner(Choice),
BehaviorDiagram(Choice),
StateDiagram(crate::behavior::state_edit::Event),
ScenarioCanvas(crate::behavior::scenario::CanvasEvent),
PlaceSelected,
HideSelected,
ToggleCollapse,
@@ -579,9 +791,10 @@ impl App {
}
fn execute(&mut self, command: Command) -> bool {
let command = crate::port_edit::repair_owner_change(self.project(), command);
let scenario_selection = self.behavior.scenario.created_selection(&command);
match self.document.execute(command) {
Ok(selected) => {
if let Some(id) = selected {
if let Some(id) = scenario_selection.or(selected) {
self.selected = id;
}
self.sync();
@@ -737,6 +950,15 @@ impl App {
])
}
pub fn update(&mut self, message: Message) -> Task<Message> {
// Scrollable geometry can change while async work or an overlay blocks
// commands. Keep the observed translation current; its passive handler
// still validates document, scenario and revision before accepting it.
if matches!(
&message,
Message::ScenarioCanvas(crate::behavior::scenario::CanvasEvent::Scrolled(..))
) {
return self.handle(message);
}
// Global subscriptions also receive keys consumed by an overlay.
if self.model_menu.is_some() && matches!(message, Message::Escape) {
self.model_menu = None;
@@ -936,8 +1158,115 @@ impl App {
task
}
}
pub(crate) fn scenario_context(&self) -> Option<crate::behavior::scenario::Context> {
let scenario = crate::behavior::selected_owner(self)?;
(self.page == Page::Behavior
&& self.project().elements.get(&scenario)?.kind() == Kind::Scenario)
.then_some(crate::behavior::scenario::Context {
document: self.canvas_document,
scenario,
revision: self.document.revision(),
epoch: self.canvas_epoch,
})
}
fn handle(&mut self, message: Message) -> Task<Message> {
match message {
Message::ScenarioCanvas(event) => {
use crate::behavior::scenario::{self, CanvasEvent};
let context = event.context();
if let CanvasEvent::Scrolled(_, area, offset) = event {
// Inner and outer scroll notifications may come from the
// same frame. Accept both across the first epoch bump,
// while still rejecting another owner/document/revision.
if self.scenario_context().is_some_and(|current| {
current.document == context.document
&& current.scenario == context.scenario
&& current.revision == context.revision
}) && self.behavior.scenario.scrolled(area, offset)
{
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
return Task::none();
}
if self.scenario_context() != Some(context) {
return Task::none();
}
match event {
CanvasEvent::Select(_, id) => {
if !self.behavior.has_pending()
&& crate::behavior::selected_behavior(self.project(), id)
== Some(context.scenario)
{
self.select(id);
self.selected_node = None;
}
}
CanvasEvent::Choose(_, tool) => {
if self.behavior.has_pending() || !self.apply() {
return Task::none();
}
self.behavior.scenario.choose_tool(context.scenario, tool);
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
mutation => {
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
{
self.status =
"Apply or cancel pending edits before changing the scenario."
.into();
return Task::none();
}
match mutation {
CanvasEvent::Begin {
slot,
sender,
receiver,
style,
..
} => {
let mut state = std::mem::take(&mut self.behavior.scenario);
let result = state.begin_message(
self.project(),
context,
slot,
sender,
receiver,
style,
);
self.behavior.scenario = state;
match result {
Ok(()) => {
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
self.status = "Choose the message exchange and apply at the highlighted sequence position.".into();
}
Err(error) => self.status = error,
}
}
CanvasEvent::Reorder {
occurrence, slot, ..
} => {
match scenario::reorder(
self.project(),
context.scenario,
occurrence,
slot,
) {
Ok(command) => {
if self.execute(command) {
self.selected = occurrence;
self.selected_node = None;
self.refresh_draft();
self.status = "Occurrence reordered within its sequence · Undo restores the previous order".into();
}
}
Err(error) => self.status = error,
}
}
_ => unreachable!(),
}
}
}
}
Message::StateDiagram(event) => return self.edit_state_diagram(event),
Message::BehaviorOwner(choice) => {
if self.apply()
@@ -1139,6 +1468,18 @@ impl App {
self.status = error;
}
}
Message::FieldValueBrowse(path, scope, page) => {
if let Some(draft) = &mut self.draft
&& draft
.value
.pointer(&path)
.is_some_and(|row| row.get("expression").is_some())
{
draft
.value_browsers
.insert(path, crate::forms::ValueBrowser { scope, page });
}
}
Message::FieldValuePath(path, key) => {
if let Some(draft) = &mut self.draft
&& let Err(error) = draft.value_path(&path, key)
@@ -2238,7 +2579,14 @@ impl App {
Err(e) => e,
};
}
Message::WindowResized(size) => self.window_size = size,
Message::WindowResized(size) => {
// The canvas can retain its capped size during a real window
// resize. Invalidate any scenario pointer/slot gesture anyway.
if self.window_size != size && self.scenario_context().is_some() {
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
self.window_size = size;
}
Message::CanvasResized(size) => {
let first_measurement = self.canvas_size.is_none();
let size_changed = self.canvas_size != Some(size);
@@ -2612,8 +2960,14 @@ impl App {
.discovered(&disk.bundle, disk.reachable.clone());
self.show_source_file(false);
self.source_diagnostics = self.document.compiled().diagnostics.clone();
self.status =
"Source project opened. Included files and comments are preserved.".into();
self.migration = self.document.compiled().decoded.migration.clone();
if self.migration.is_some() {
self.status = "Imported earlier source schema. Review Model checks; Save all explicitly writes the upgraded source while retaining comments and includes.".into();
self.page = Page::Validation;
} else {
self.status =
"Source project opened. Included files and comments are preserved.".into();
}
}
Err(error) => {
self.canvas_document = Id::new_v4();
@@ -3243,7 +3597,9 @@ fn read_snapshot(path: PathBuf) -> Result<LoadedSnapshot, String> {
&& let Some(source_files::DiskExpectation::Bytes(bytes)) =
disk.expectations.get(&disk.bundle.entry)
&& let Ok(decoded) = persistence::decode_with_report_limited(bytes, LIMITS.model)
&& decoded.migration.is_some()
// Source1/2 require the historical single-document adapters.
// Source3+ must retain its authored bundle and token migration.
&& decoded.migration.as_ref().is_some_and(|report| report.from_schema <= 2)
{
return Ok(LoadedSnapshot::Legacy(
path,
@@ -3328,6 +3684,97 @@ mod source_project_tests {
Ok(())
}
#[test]
fn source3_native_open_preserves_report_comments_nesting_and_save_reopen() {
let original = include_str!("../../oa-core/tests/fixtures/v3-data-contract.arcadia");
for split in [false, true] {
let directory = Directory::new();
let path = directory.0.join("project.arcadia");
let mut authored = std::collections::BTreeMap::new();
if split {
let start = original.find("{\n").unwrap() + 2;
let end = original.rfind('}').unwrap();
authored.insert(
"project.arcadia",
format!(
"{} include \"dictionary.arcadia\";\n}}\n",
&original[..start]
),
);
authored.insert("dictionary.arcadia", original[start..end].to_owned());
} else {
authored.insert("project.arcadia", original.to_owned());
}
for (file, source) in &authored {
std::fs::write(directory.0.join(file), source).unwrap();
}
let mut app = App::with_project(Project::empty());
send(
&mut app,
Message::Loaded(Ok(Some(read_snapshot(path.clone()).unwrap()))),
);
assert_eq!(app.migration.as_ref().unwrap().from_schema, 3);
assert_eq!(app.page, Page::Validation);
assert_eq!(app.path, Some(path.clone()));
assert_eq!(app.document.bundle().files.len(), authored.len());
assert!(
app.dirty(),
"accepted source migration needs explicit saving"
);
for (file, source) in &authored {
assert_eq!(
std::fs::read_to_string(directory.0.join(file)).unwrap(),
*source
);
}
let text = app
.document
.bundle()
.files
.values()
.cloned()
.collect::<String>();
assert!(text.contains("// Reused record parameters retain their own identities."));
assert!(text.contains("// Frozen authored schema3 compatibility input."));
let packet = app
.project()
.elements
.values()
.find(|e| e.alias == "packet")
.unwrap()
.id;
let origin = app
.project()
.elements
.values()
.find(|e| e.alias == "origin")
.unwrap()
.id;
assert_eq!(
app.document.compiled().index.lexical_owners.get(&origin),
Some(&packet)
);
let before = app.document.snapshot();
save(&mut app, path.clone()).unwrap();
assert!(!app.dirty());
for (file, source) in &before.bundle.files {
assert_eq!(
std::fs::read_to_string(directory.0.join(file)).unwrap(),
*source
);
}
let mut reopened = App::with_project(Project::empty());
send(
&mut reopened,
Message::Loaded(Ok(Some(read_snapshot(path).unwrap()))),
);
assert!(reopened.migration.is_none());
assert_eq!(reopened.project(), app.project());
assert_eq!(reopened.document.bundle(), app.document.bundle());
assert!(!reopened.dirty());
}
}
fn pending_journal(directory: &Directory, before: Option<&str>) -> (PathBuf, PathBuf) {
let path = directory.0.join("project.arcadia");
let journal = directory.0.join(".arcadia-save-project.arcadia.journal");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+119 -45
View File
@@ -11,10 +11,11 @@ pub struct FieldPathKey {
pub struct FieldPathChoice {
pub key: FieldPathKey,
pub label: String,
pub picker_label: String,
}
impl std::fmt::Display for FieldPathChoice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.label)
f.write_str(&self.picker_label)
}
}
@@ -49,8 +50,7 @@ fn named_choice(project: &Project, entry: data::CarriedFieldPath) -> FieldPathCh
.map(|e| format!("{} (@{})", e.name, e.alias))
.unwrap_or_else(|| id.to_string())
})
.collect::<Vec<_>>()
.join(" → ");
.collect::<Vec<_>>();
let direction =
if data::item_mechanism(project, entry.item) == Some(ExchangeMechanism::Operation) {
match entry.direction {
@@ -63,54 +63,97 @@ fn named_choice(project: &Project, entry: data::CarriedFieldPath) -> FieldPathCh
} else {
""
};
let terminal = qualified
.last()
.expect("A field path always has a terminal");
let owner_path = qualified[..qualified.len() - 1].join(" → ");
let picker_label = format!(
"{terminal}{direction} — {}{}",
project.named(entry.item),
if owner_path.is_empty() {
String::new()
} else {
format!(" / {owner_path}")
},
);
FieldPathChoice {
key: FieldPathKey {
path: entry.path,
field: entry.field,
},
label: format!("{} / {qualified}{direction}", project.named(entry.item)),
label: format!(
"{} / {}{direction}",
project.named(entry.item),
qualified.join(" → ")
),
picker_label,
}
}
// A bounded picker must expose its distinguishing field before a long path.
// Keep the full address separately for the wrapping detail text.
fn sorted_choices(mut choices: Vec<FieldPathChoice>) -> Vec<FieldPathChoice> {
choices.sort_by_cached_key(|choice| choice.picker_label.to_lowercase());
choices
}
#[cfg(test)]
pub fn choices(
project: &Project,
exchange: &Relation,
phase: &MessageKind,
) -> Result<Vec<FieldPathChoice>, String> {
Ok(data::try_carried_field_paths(project, exchange)?
.into_iter()
.filter(|entry| phase_allows(project, entry, phase))
.map(|entry| named_choice(project, entry))
.collect())
Ok(sorted_choices(
data::try_carried_field_paths(project, exchange)?
.into_iter()
.filter(|entry| phase_allows(project, entry, phase))
.map(|entry| named_choice(project, entry))
.collect(),
))
}
/// Direct carried fields only: an unrelated deep branch must not prevent
/// browsing a root field. Preserve the core resolver's first carried owner.
pub fn roots(project: &Project, exchange: &Relation, phase: &MessageKind) -> Vec<FieldPathChoice> {
let mut seen = std::collections::BTreeSet::new();
exchange
.body
.items()
.iter()
.flat_map(|item| {
data::effective_fields(project, *item)
.into_iter()
.filter_map(|field| {
let SharedBody::DataField { direction, .. } = field.shared()? else {
return None;
};
Some(data::CarriedFieldPath {
item: *item,
path: vec![],
field: field.id,
direction: *direction,
sorted_choices(
exchange
.body
.items()
.iter()
.flat_map(|item| {
data::effective_fields(project, *item)
.into_iter()
.filter_map(|field| {
let SharedBody::DataField { direction, .. } = field.shared()? else {
return None;
};
Some(data::CarriedFieldPath {
item: *item,
path: vec![],
field: field.id,
direction: *direction,
})
})
})
})
.filter(|entry| seen.insert(entry.field))
.filter(|entry| phase_allows(project, entry, phase))
.map(|entry| named_choice(project, entry))
.collect()
})
.filter(|entry| seen.insert(entry.field))
.filter(|entry| phase_allows(project, entry, phase))
.map(|entry| named_choice(project, entry))
.collect(),
)
}
/// Browse one record at a time, including explicitly chosen recursive hops.
pub fn at_scope(
project: &Project,
exchange: &Relation,
phase: &MessageKind,
scope: Option<&FieldPathKey>,
) -> Result<Vec<FieldPathChoice>, String> {
match scope {
Some(key) => children(project, exchange, phase, key),
None => Ok(roots(project, exchange, phase)),
}
}
/// Resolve authored finite paths as well as the finite representative picker list.
@@ -166,20 +209,22 @@ pub fn children(
}
let mut path = key.path.clone();
path.push(key.field);
Ok(data::effective_fields(project, *record)
.into_iter()
.map(|field| {
named_choice(
project,
data::CarriedFieldPath {
item: entry.item,
path: path.clone(),
field: field.id,
direction: entry.direction,
},
)
})
.collect())
Ok(sorted_choices(
data::effective_fields(project, *record)
.into_iter()
.map(|field| {
named_choice(
project,
data::CarriedFieldPath {
item: entry.item,
path: path.clone(),
field: field.id,
direction: entry.direction,
},
)
})
.collect(),
))
}
#[cfg(test)]
@@ -482,4 +527,33 @@ pub(crate) mod tests {
.contains("128")
);
}
#[test]
fn recursive_picker_starts_with_distinct_leaf_names_and_sorts_them() {
let (f, next) = recursive_payload();
let exchange = &f.project.relations[&f.exchange];
let context = FieldPathKey {
path: vec![f.origin, next, next],
field: next,
};
let visible = children(&f.project, exchange, &MessageKind::Call, &context).unwrap();
let leaf = visible
.iter()
.find(|choice| choice.key.field == f.leaf)
.unwrap();
assert!(leaf.to_string().starts_with("latitude (@latitude)"));
assert!(leaf.label.contains("origin (@origin) → next (@next)"));
assert!(
visible.windows(2).all(
|pair| pair[0].to_string().to_lowercase() <= pair[1].to_string().to_lowercase()
)
);
assert_ne!(
visible[0].to_string().chars().take(24).collect::<String>(),
visible[1].to_string().chars().take(24).collect::<String>()
);
assert_eq!(
resolve(&f.project, exchange, &MessageKind::Call, &leaf.key),
Some(leaf.clone())
);
}
}
+19 -4
View File
@@ -32,6 +32,12 @@ pub struct Input {
pub raw: String,
pub editor: text_editor::Content,
}
#[derive(Clone, Default)]
pub struct ValueBrowser {
pub scope: Option<crate::data_values::FieldPathKey>,
pub page: usize,
}
pub struct Draft {
pub kind: RecordKind,
pub original: Id,
@@ -40,6 +46,7 @@ pub struct Draft {
pub inputs: BTreeMap<String, Input>,
pub exact_strings: bool,
port_contract_drafts: BTreeMap<String, Value>,
pub value_browsers: BTreeMap<String, ValueBrowser>,
}
impl Draft {
@@ -77,11 +84,14 @@ impl Draft {
inputs: BTreeMap::new(),
exact_strings: false,
port_contract_drafts: BTreeMap::new(),
value_browsers: BTreeMap::new(),
};
draft.rebuild();
Some(draft)
}
fn rebuild(&mut self) {
// Array/variant edits can change the meaning of a row path.
self.value_browsers.clear();
self.inputs.clear();
fn visit(value: &Value, path: &str, out: &mut BTreeMap<String, Input>) {
match value {
@@ -710,10 +720,15 @@ fn reference_ids(draft: &Draft, project: &Project, path: &str) -> Vec<Id> {
..
},
..
} => project
.relations
.get(id)
.and_then(|r| crate::data_values::choices(project, r, message_kind).ok()),
} => project.relations.get(id).and_then(|r| {
let scope = address_path.split_last().map(|(&field, path)| {
crate::data_values::FieldPathKey {
path: path.to_vec(),
field,
}
});
crate::data_values::at_scope(project, r, message_kind, scope.as_ref()).ok()
}),
_ => None,
})
.unwrap_or_default()
+211 -81
View File
@@ -330,97 +330,152 @@ fn message_value<'a>(
} => project.relations.get(id).map(|r| (r, message_kind)),
_ => None,
});
let choices = context
.map(|(r, phase)| crate::data_values::choices(project, r, phase))
.unwrap_or_else(|| Ok(vec![]));
let mut out = column![text("Qualified carried field").size(11).color(style::MUTED)].spacing(6);
match choices {
Err(error) => out = out.push(text(error).size(11).color(style::MUTED)),
Ok(mut choices) => {
let current_key = current.as_ref().map(|v| crate::data_values::FieldPathKey {
path: v.path.clone(),
field: v.field,
});
let mut selected = current_key
let current_key = current
.as_ref()
.map(|value| crate::data_values::FieldPathKey {
path: value.path.clone(),
field: value.field,
});
let selected = context.and_then(|(exchange, phase)| {
current_key
.as_ref()
.and_then(|key| crate::data_values::resolve(project, exchange, phase, key))
});
let browser = draft
.value_browsers
.get(path)
.cloned()
.unwrap_or_else(|| ValueBrowser {
scope: selected
.as_ref()
.and_then(|key| choices.iter().find(|c| &c.key == key))
.cloned()
.or_else(|| {
context.and_then(|(r, phase)| {
current_key
.as_ref()
.and_then(|key| crate::data_values::resolve(project, r, phase, key))
})
});
if let Some(choice) = &selected
&& !choices.iter().any(|c| c.key == choice.key)
{
choices.push(choice.clone());
}
if selected.is_none()
&& let Some(key) = current_key.clone()
&& !key.field.is_nil()
{
out = out.push(text("Current address needs review. Choose an explicit path to resolve a legacy, ambiguous or incompatible value.").size(11).color(style::MUTED));
let retained = crate::data_values::FieldPathChoice {
label: format!(
"Current address · {} / {}",
key.path
.iter()
.map(|id| project.named(*id))
.collect::<Vec<_>>()
.join(" → "),
project.named(key.field)
),
key,
};
selected = Some(retained.clone());
choices.push(retained);
}
let owned = path.to_owned();
out = out.push(
pick_list(choices, selected, move |choice| {
Message::FieldValuePath(owned.clone(), choice.key)
})
.placeholder("Choose a carried field path…")
.text_size(12)
.padding(8)
.width(Fill),
);
}
.and_then(|choice| crate::data_values::parent(&choice.key)),
page: 0,
});
let mut out = column![text("Qualified carried field").size(11).color(style::MUTED)].spacing(6);
if let Some(choice) = &selected {
out = out.push(text(choice.label.clone()).size(11));
} else if let Some(key) = &current_key
&& !key.field.is_nil()
{
out = out.push(
text(format!(
"Current address needs review: {} / {}. Choose an explicit field.",
key.path
.iter()
.map(|id| project.named(*id))
.collect::<Vec<_>>()
.join(" → "),
project.named(key.field)
))
.size(11)
.color(style::MUTED),
);
}
if let (Some((exchange, phase)), Some(current)) = (context, current) {
let key = crate::data_values::FieldPathKey {
path: current.path,
field: current.field,
};
if crate::data_values::resolve(project, exchange, phase, &key).is_some() {
if let Some(parent) = crate::data_values::parent(&key) {
out = out.push(
button(text("Up to enclosing field").size(11))
if let Some((exchange, phase)) = context {
let location = browser
.scope
.as_ref()
.and_then(|key| crate::data_values::resolve(project, exchange, phase, key))
.map(|choice| choice.label)
.unwrap_or_else(|| "Root carried fields".into());
out = out.push(
text(format!("Browse: {location}"))
.size(11)
.color(style::MUTED),
);
if let Some(scope) = &browser.scope {
let mut navigation = row![button(text("↑ Up").size(11)).style(style::nav).on_press(
Message::FieldValueBrowse(path.into(), crate::data_values::parent(scope), 0)
)]
.spacing(4);
if !scope.path.is_empty() {
navigation = navigation.push(
button(text("Root fields").size(11))
.style(style::nav)
.on_press(Message::FieldValuePath(path.into(), parent)),
.on_press(Message::FieldValueBrowse(path.into(), None, 0)),
);
}
match crate::data_values::children(project, exchange, phase, &key) {
Ok(children) if !children.is_empty() => {
let path = path.to_owned();
out = out.push(navigation);
}
match crate::data_values::at_scope(project, exchange, phase, browser.scope.as_ref()) {
Ok(choices) => {
let count = choices.len();
let (visible, page, pages) = field_page(choices, browser.page);
// Selection detail stays visible independently of browser location/page.
let visible_selection = selected
.clone()
.filter(|choice| visible.iter().any(|c| c.key == choice.key));
let owned = path.to_owned();
out = out.push(
pick_list(visible, visible_selection, move |choice| {
Message::FieldValuePath(owned.clone(), choice.key)
})
.placeholder("Choose a field here…")
.text_size(12)
.padding(8)
.width(Fill),
);
if pages > 1 {
out = out.push(
pick_list(
children,
None::<crate::data_values::FieldPathChoice>,
move |choice| Message::FieldValuePath(path.clone(), choice.key),
)
.placeholder("Choose a field inside this record…")
.text_size(12)
.padding(8)
.width(Fill),
row![
button(text("Previous").size(11))
.style(style::nav)
.on_press_maybe((page > 0).then(|| Message::FieldValueBrowse(
path.into(),
browser.scope.clone(),
page - 1
))),
text(format!("{} / {pages} · {count} fields", page + 1)).size(11),
button(text("Next").size(11))
.style(style::nav)
.on_press_maybe((page + 1 < pages).then(|| {
Message::FieldValueBrowse(
path.into(),
browser.scope.clone(),
page + 1,
)
}))
]
.spacing(4)
.align_y(iced::Alignment::Center),
);
} else if count == 0 {
out = out.push(
text("No compatible fields at this location.")
.size(11)
.color(style::MUTED),
);
}
Err(error) => out = out.push(text(error).size(11).color(style::MUTED)),
_ => {}
}
Err(error) => out = out.push(text(error).size(11).color(style::MUTED)),
}
if let Some(choice) = &selected
&& browser.scope.as_ref() != Some(&choice.key)
&& crate::data_values::children(project, exchange, phase, &choice.key)
.is_ok_and(|children| !children.is_empty())
{
out = out.push(
button(
text(format!(
"Browse fields in {} →",
project.named(choice.key.field)
))
.size(11),
)
.style(style::nav)
.on_press(Message::FieldValueBrowse(
path.into(),
Some(choice.key.clone()),
0,
)),
);
}
} else {
out = out.push(
text("Choose a message exchange to browse its carried fields.")
.size(11)
.color(style::MUTED),
);
}
if let Some(expression) = value.get("expression") {
out = out.push(super::property(
@@ -435,6 +490,25 @@ fn message_value<'a>(
out.into()
}
const FIELDS_PER_PAGE: usize = 24;
fn field_page(
choices: Vec<crate::data_values::FieldPathChoice>,
requested: usize,
) -> (Vec<crate::data_values::FieldPathChoice>, usize, usize) {
let pages = choices.len().div_ceil(FIELDS_PER_PAGE).max(1);
let page = requested.min(pages - 1);
(
choices
.into_iter()
.skip(page * FIELDS_PER_PAGE)
.take(FIELDS_PER_PAGE)
.collect(),
page,
pages,
)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -490,6 +564,25 @@ mod tests {
format!("{path}/expression"),
"newly typed".into(),
));
let before_browse = app.draft.as_ref().unwrap().materialize().unwrap();
for (scope, page) in [
(None, 0),
(
Some(crate::data_values::FieldPathKey {
path: vec![f.origin],
field: next,
}),
999,
),
] {
let _ = app.update(Message::FieldValueBrowse(path.into(), scope, page));
let _ = super::super::view(app.draft.as_ref().unwrap(), app.project());
assert_eq!(
app.draft.as_ref().unwrap().materialize().unwrap(),
before_browse
);
assert_eq!(app.project(), &f.project);
}
let _ = app.update(Message::FieldValuePath(
path.into(),
crate::data_values::FieldPathKey {
@@ -553,6 +646,43 @@ mod tests {
let _ = super::super::view(app.draft.as_ref().unwrap(), app.project());
}
#[test]
fn field_browser_pages_every_direct_field_without_flattening_other_records() {
let f = crate::data_values::tests::wide_payload();
let exchange = &f.project.relations[&f.exchange];
let roots =
crate::data_values::at_scope(&f.project, exchange, &MessageKind::Call, None).unwrap();
assert!(roots.len() > FIELDS_PER_PAGE);
assert!(roots.iter().all(|choice| choice.key.path.is_empty()));
let scope = crate::data_values::FieldPathKey {
path: vec![],
field: f.origin,
};
let children =
crate::data_values::at_scope(&f.project, exchange, &MessageKind::Call, Some(&scope))
.unwrap();
assert!(children.len() > FIELDS_PER_PAGE);
assert!(
children
.iter()
.all(|choice| choice.key.path == vec![f.origin])
);
for choices in [roots, children] {
let (_, _, pages) = field_page(choices.clone(), 0);
let visited: Vec<_> = (0..pages)
.flat_map(|page| {
let (visible, actual, _) = field_page(choices.clone(), page);
assert_eq!(actual, page);
assert!(visible.len() <= FIELDS_PER_PAGE);
visible
})
.collect();
assert_eq!(visited, choices);
assert_eq!(field_page(choices, usize::MAX).1, pages - 1);
}
assert_eq!(field_page(vec![], usize::MAX), (vec![], 0, 1));
}
#[test]
fn numeric_bound_drafts_keep_partial_and_full_precision_values_out_of_accepted_state() {
let mut p = Project::empty();
+6
View File
@@ -63,3 +63,9 @@ State camera/tool lifetime follows effective machine and diagram identity, even
when selection changes through Catalog or Layer outside Behavior. Reset only on
actual context change; preserve same-view return. Independent review reproduced
the leak and native Windows/Linux tests verified the correction in316dc55.
- Carried-value property browsing follows one exact record scope at a time, with
bounded pages and a separate read-only browser location. Flattened representative
paths were proven ambiguous in native recursive/reused data. Browsing does not
change the selected field or expression; choosing a field remains an explicit
draft edit through the ordinary validated command. No persistent browser schema.
+68 -34
View File
@@ -2,6 +2,23 @@
## Objective and phase
Direct scenario work is now in isolated `codex/scenario-direct`, checkout
`D:/CodexBuilds/Capella2-scenario-direct`, base ab661f2, dedicated target
`D:/CodexBuilds/Capella2-scenario-target`. Scope and acceptance are recorded in
release-scenario-editing.md. Existing scene/order semantics and typed drafts are
retained; direct drawing, explicit insertion, same-parent reorder and correlated
activation rendering are implemented in d1d8f0b, with independent-review fixes
in 7a66607 for stale scroll slots, actual Iced scroll coordinates, operand-specific
activation segments and repeated picker cost, plus e7e37d4 for passive scroll
observations during async save. DATA b35dede is integrated in dfd2e41 without
conflicts. Combined Windows desktop tests pass 286/286, strict all-target desktop
Clippy and optimized build pass; a copied artifact/manifest is frozen under
`D:/CodexBuilds/Capella2-release1-01a06e2e/ui-scenario-data-windows-dfd2e41`. Exact artifact
hash, before/after failures and timings are in release-scenario-editing.md.
Focused independent recheck reports no remaining medium/high finding. Native
Windows/Linux trials remain pending and root-owned; this branch claims no native
acceptance and does not close process/chain work.
The first-release goal remains active and incomplete. Deliver usable Rust/Iced
Operational Analysis and System Analysis/System Architecture, equivalent graphical,
textual and REST semantics, artifact traceability, directly editable behavior,
@@ -11,8 +28,10 @@ attachment10134df9-bea2-4c02-a820-67b53d6b86f7/goal-objective.md.
PLAN.md retains the entire release scope; no subsystem acceptance completes it.
Direct state-machine editing is accepted through production316dc55. The main
checkout was fast-forwarded to acceptance a8f451e. R6 data/interface contracts are now
being implemented in a separate checkout; they are not accepted or released.
checkout was fast-forwarded to acceptance a8f451e. R6 data/interface contracts
through b35dede are now integrated into the isolated direct-scenario checkout.
Root retains final main integration and acceptance; the combined direct scenario
has not yet completed native trials.
Meaningful implementation and runtime progress continues; no external blocker exists.
## Accepted foundations
@@ -76,23 +95,17 @@ Dedicated target D:/CodexBuilds/Capella2-data-target. Contract freeze is schema/
with full data payload and dictionary slices together; existing typed ports remain.
See release-data-implementation.md and release-data-contract.md in that checkout.
Core is committed0b58107; source/migration1230ab8. Root API/docs are integrated
on the same working tree; desktop remains in flight and R6 is not accepted.
Core15 R6 +28workflow tests passed again after final allocation-arrow suppression.
Linux0b58107 full core307 tests passed (one explicit probe ignored by default),
with strict all-target core Clippy. Earlier13ports/14scenarios and115 source/lib
tests also passed. Desktop final257 tests and strict all-target Clippy pass;
optimized Windows desktop build b403039 passed. Its SHA256 is recorded in the R6 verification note; native acceptance remains pending.
R6 core/source/desktop are frozen at ab661f2. Full Windows607 tests and Linux610
tests pass (one explicit benchmark ignored on each platform); strict workspace
all-target Clippy passes on both. Optimized desktop/server Linux build also passed.
Windows desktop261 tests include the bounded scenario value editor. Exact logs,
binaries and runtime evidence: release-data-verification.md.
Root41 server tests passed; a fourth R6 HTTP workflow now also passes, alongside all4 data HTTP tests and strict server Clippy. The original3 actual HTTP workflows for the published
data example, protocol/artifact identity, atomic rejected defaults, query/export,
reidentify/delete/restart, source3 bundle migration reports and old coupled3
envelope upgrade without write/revision. Tests exposed dropped bundle reports and
rejected legitimate old source/model pairs; both root fixes passed retest.
Strict reST HTML generation and81 OpenAPI schema definitions passed. Actual frozen
server32033FFE imported the published example; its complete Project/StoredProject
responses passed served/current JSON Schema validation and obsolete shapes rejected.
Evidence: api-r6-contract and r6-server-tests-after.log under the release directory.
All4 R6 actual HTTP workflows pass, covering data protocols/artifact identity,
rejected defaults, qualified Call/Reply paths, validation locations, source edits,
reidentification, source3 migration and restart persistence. The published example's
actual server responses passed served/local OpenAPI validation; obsolete shapes
reject. Strict reST rendering and81 OpenAPI schemas passed.
Independent core review found association transition closure pulling unrelated
types and missing empty-enum warning; both corrected and focused tests passed.
@@ -106,14 +119,24 @@ pages replaces unbounded inherited rows; recursive paths support explicit drill-
and imported finite paths; same-phase styles/same-exchange reselection preserve
valid drafts. Read-only follow-up found no new path/style defect. Paging resets
only on actual layer/document changes; its regression failed before and passes now.
Root found another unresolved scale issue: scenario creation still enumerates every
carried field into both a dropdown and individual input rows. A bounded focused value editor is now implemented and under automated/independent review; actual native operation remains required before R6 acceptance.
The bounded scenario editor replaces whole-recursion enumeration and per-field
inputs with24-row browsing and a single focused value draft. The reply call picker
no longer filters alternative calls using values belonging to the old exchange.
Automated and independent checks pass; native scenario checks are next.
Current ownership: drawing_correctness owns desktop except root-owned guide.rs and
the dedicated Windows Cargo target. state_architecture_review independently reviews
R6 spec/API consistency; its data_view.rs ownership has ended. data_core runs an
isolated R3 typed-baseline experiment; Linux R6 core checkout is idle and verified.
Root owns API/OpenAPI/reST, docs, integration and native input. No concurrent writers.
Root exclusively operates native R6. The Windows13-object data model has saved
native bounds/default repair, a typed physical flow/field, allocation protocol
edit, allocation removal/Undo, and inherited-owner navigation. Association endpoint
labels/selectability were exercised. The1100x700 data layout has been inspected.
A separate53-object recursive-operation fixture was prepared with core APIs and
round-tripped for upcoming native scenario trials; fixture construction is not
claimed as native authoring. See the verification note for remaining gates.
Parallel isolated work: drawing_correctness implements direct scenario gestures in
Capella2-scenario-direct. data_core owns typed semantic review core and
state_architecture_review owns source5/migration in Capella2-semantic-review.
The typed facet C design won the bounded experiment/criticism round; its production
plan and decision are recorded in that checkout. No overlapping writers or targets.
1. State evidence is committed and main fast-forwarded to a8f451e; DATA received
the same evidence in c5276b1. Finish native data-authoring acceptance and bounded
@@ -121,8 +144,8 @@ Root owns API/OpenAPI/reST, docs, integration and native input. No concurrent wr
2. Finish R6 core/source/desktop, integrate actual REST/schema/reST, independent
semantic/correctness/UX review, Windows/Linux checks and real data authoring.
3. Implement R3 typed semantic drift/reconciliation after R6 freezes. Current
baselines/reconciliation only cover prose. Prefer historical typed-body snapshots
and finite proposals; avoid a duplicate recursive semantic schema without evidence.
baselines/reconciliation only cover prose. Use the experimentally selected typed facet review design C; the semantic
integration checkout records the full scope and source5 migration plan.
4. Complete direct scenario/process gestures, persistent coverage/filters,
Home/recent/recovery, hosted desktop ETag workflows and offline guidance.
5. Fresh final requirements/domain/correctness/native UX/architecture review; full
@@ -130,13 +153,13 @@ Root owns API/OpenAPI/reST, docs, integration and native input. No concurrent wr
## Environment and native ownership
Native input is paused after physical Escape; no R6 native trial has run.
Root exclusively owns native input. Latest saved Windows trial is window1706782,
ui-state-navigation-windows,49objects/28links/1 expected missing-owner warning.
Linux navigation trial3148598 closed normally, exec46107 exit0; baseline Linux
trial721472 also closed normally, exec72680 exit0. Independent Windows3672536 is
saved at1100x700. Older root drawing windows are saved. Re-inventory before input.
Protected user7210312/6228978 were absent; do not reset/save/close if they return.
The user explicitly reauthorized native testing after Escape. Root's f5467e5 R6
window3476204 and exec93768 closed normally after verified source migration and
55-object recursive scenario reopen; no runtime output. The property-browser
replacement build b35dede has passed the root's Windows native retest. The
combined direct-scenario binary is frozen for its separate pending native trial.
Original Capella is present. Older protected user7210312/6228978 were absent;
do not reset/save/close them if they return. Re-inventory before switching apps.
Evidence: D:/CodexBuilds/Capella2-release1-01a06e2e. State source checkout/target:
D:/CodexBuilds/Capella2-state-integration and -target. Linux source/target:
@@ -147,3 +170,14 @@ Windows: cargo test --locked --workspace; cargo clippy --locked --workspace
--all-targets -- -D warnings; cargo build --locked -p oa-desktop -p oa-server.
WSL Ubuntu24.04 uses target/linux-cargo.toml and Mesa software Vulkan for native
WSLg. No remote push or usage reset authorized. No broad recursive target deletion.
Latest R6 checkpoint: b35dede Windows264 desktop tests, strict Clippy, optimized
build and root-owned native retest pass for the one-record property browser. Paging and
location changes retain accepted model and typed expressions. f5467e5 Windows
and Linux263 desktop tests, strict Clippy and optimized release passed. Native
Windows source3 migration preserved comments/nesting through explicit Save All
and clean source4 reopen. Native recursive55-object scenario values and paired
Call/Reply history were verified; flat property paths prompted the latest fix.
The independent bounded-browser review reports no remaining medium/high finding.
Linux native R6 workflows remain a root-owned acceptance gate. See
release-data-verification.md. Main remains a8f451e; R6 is not yet accepted.
+12 -8
View File
@@ -39,9 +39,13 @@ requires native workflows, semantic evaluators and fresh independent review.
whole-model equality or flat reformatting. Independent counterexamples were fixed.
- R3 existing-target reconciliation remains name/description only. R6 data shape
expansion is active implementation, not accepted functionality.
- R6 core/source and actual HTTP semantics pass focused Windows and full Linux
core checks; desktop257 tests pass after independent draft/path/paging fixes.
Native R6 acceptance and bounded scenario-value authoring remain outstanding.
- R6 ab661f2 full suites passed Windows607/Linux610; f5467e5 desktop263 and
strict Clippy/release passed on both platforms. Native Windows data editing,
typed defaults, allocations, recursive qualified Call/Reply values, history and
saved reopening passed. Source3 migration now preserves comments and nesting.
Native found flat property browsing still ambiguous; one-record browsing with
bounded pages now passes264 desktop tests/Clippy, pending rebuilt native checks.
Linux native R6 acceptance remains outstanding.
## Assumptions and open questions
@@ -51,11 +55,11 @@ requires native workflows, semantic evaluators and fresh independent review.
gestures still need their own occurrence/order semantics and task verification.
- Routing offers a usable initial path plus manual pins; arbitrary dense diagrams
are not guaranteed crossing-free. Measured dense full solve119ms remains a limit.
- R3 typed historical bodies plus finite proposals appear simpler than a separate
recursive semantic-value schema. An isolated probe supports scalar partial review
and deleted historical references, but ordered membership/order acknowledgements
need separate typed order checkpoints. Historical source reference parsing is
still unimplemented; this remains a candidate awaiting independent review.
- R3 selected design C uses finite self-tagged typed facets, separate membership
and order acknowledgements, and transient constructor groups. Source5 historical
references are implemented in the isolated semantic checkout. Independent review
found narrow-acknowledgement/canonical-agreement/rollup defects; the worker has
added regressions and fixes. Full catalog, GUI/API/spec and acceptance remain.
## Decisions and current strategy
+112 -43
View File
@@ -1,50 +1,119 @@
# R6 data contracts — verification in progress
# R6 data contracts — native verification in progress
R6 is not accepted. Main remains at state-editing acceptance a8f451e. The updated
objective10134df9-bea2-4c02-a820-67b53d6b86f7 was read fully on2026-09-06; product
scope is unchanged and reasoning effort must match the delegated task. The full
first release remains incomplete. Native input is paused after physical Escape.
R6 is not accepted. Main remains at a8f451e. The full first-release goal remains
active and incomplete. User explicitly reauthorized native testing after Escape.
## Current evidence
## Frozen production and automated evidence
- Core0b58107: Linux307 tests pass, one explicitly ignored benchmark; strict
all-target core Clippy passes. Windows15 R6+28 workflows pass after final
allocation-edge filtering. Source migration/compatibility evidence is retained.
- Desktopb403039: Windows257 tests, strict all-target desktop Clippy and release
build pass. Binary D:/CodexBuilds/Capella2-data-target/release/arcadia-studio.exe,
SHA256 d4aa6ae7d36f429c0cb639cdf57e33cb8b99d158a9629c95a1510d24e406fab5.
The later bounded scenario editor is not covered by this binary.
- Optimized empty inheritance chains250/500/1000: Windows21.070/89.088/391.590ms;
Linux22.381/111.662/408.129ms. These are single-run validation measurements.
- Server41 prior tests passed. The new qualified Call/Reply workflow and all4 R6
HTTP tests now pass with strict all-target server Clippy. Actual HTTP proves
distinct reused/inherited/recursive field paths, preserved expression whitespace,
phase/path422 with unchanged ETag/model/source/disk, source edits, enclosing-hop
and terminal/base reidentification, correlation and exact restart persistence.
Initial new-test failures were fixture misuse of untyped Port construction and
incorrect assumption that transaction errors carry structured issues. Tests now
use make_port and the documented command_rejected/detail plus validation endpoint.
- Published data example was imported into actual frozen server32033FFE. Complete
Project/StoredProject responses passed served/local OpenAPI validation; obsolete
shapes rejected. reST strict rendering and81 OpenAPI schemas passed.
- Independent spec review found one stale identity-only port-compatibility paragraph;
fixed to directional explicit ancestry and strict HTML rendering passed. No other
material R6 spec/API contradiction found in that review.
Data integration ab661f2: Windows607 and Linux610 workspace tests pass, one explicit
benchmark ignored each; strict workspace all-target Clippy passes on both.
Windows261 desktop tests include the focused bounded scenario editor; Linux release
desktop/server build passed. Logs under D:/CodexBuilds/Capella2-release1-01a06e2e:
r6-workspace-windows-ab661f2.log and r6-workspace-windows-clippy-ab661f2.log.
Linux logs in Capella2-data-linux-review/target/linux-verification:
workspace-ab661f2.log, workspace-clippy-ab661f2.log, release-ab661f2.log.
Logs are under D:/CodexBuilds/Capella2-release1-01a06e2e:
r6-http-paths-final.log, r6-http-all-data.log, r6-http-paths-clippy.log,
r6-server-tests-after.log and api-r6-contract. Linux core evidence is under
D:/CodexBuilds/Capella2-data-linux-review/target/linux-verification/report.md.
Windows frozen executable ui-r6-windows/arcadia-studio.exe SHA256
C3ABD9E550E1AD6824DFE273EB27A3ABB35C5C0DA97C819DCB0AB2586AF5450C.
Current native window1835476, process session37598. No output observed; runtime.log
has not been created by Tee-Object (do not claim an empty log file).
## Active checks and remaining gates
All4 actual R6 HTTP tests pass with strict server Clippy: published data example,
protocol/artifact identity, rejected defaults, qualified reused/inherited/recursive
Call/Reply paths, phase/path422 with unchanged ETag/model/source/disk, source edits,
hop/terminal/base reidentification, correlation and restart persistence. Source3
coupled/source bundle migrations report correctly and opening alone does not write.
Logs: r6-http-paths-final.log, r6-http-all-data.log, r6-http-paths-clippy.log.
Initial fixture errors (untyped Port and transaction-error envelope assumption)
were corrected before passing. Published example responses from server32033FFE
passed served/local OpenAPI validation; obsolete shapes rejected. Strict reST HTML
and81 OpenAPI schemas passed. One stale port compatibility paragraph was corrected.
The isolated Linux checkout was fast-forwarded to b403039; full workspace tests are
running in exec98587 with its dedicated data-linux-target. Initial filesystem wait
was verified as a live Cargo process in p9_client_rpc; it subsequently compiled.
Do not restart from an observation timeout. Bounded scenario editor production and
regressions are being tested by the desktop worker. Independent review found stale
values hiding an alternative reply call; its fix is included in that worker pass.
Optimized empty inheritance chains250/500/1000: Windows21.070/89.088/391.590ms;
Linux22.381/111.662/408.129ms. Single-run core validation timings, not UI latency.
Before acceptance: integrated Windows/Linux checks, native data/association/scenario
value workflows at normal/minimum sizes, source3 migration diagnostics, Save/reopen,
and closure of material review findings. No R6 visual or real-app acceptance claim.
## Personally observed Windows native workflows
Loaded the isolated Engineering data source fixture. Its architecture view shows
nested fields, association role/multiplicity labels, self-reference navigation and
selectable endpoint labels. No selection-induced dirty state observed.
Voltage maximum48→12 with default24 rejects when navigating; entered12 survives,
accepted summary remains0..48/default24, and a located default error is shown.
Repair default→12 applies together; Undo restores both old values, Redo restores
both new values. Created Cooling water with Material/Flow and contained Mass flow
rate Real1..1, unit kg/s, explicit default0.0. Save All wrote an included sa/types.arcadia
file with matching typed values, leaving13objects/4links/1 expected missing-System
warning. Navigation allocation changed Synchronous→Asynchronous; Monitoring's
separate Asynchronous/Read allocation is preserved. Removing Monitoring allocation
keeps Navigate; Undo restores the allocation and original checks. Followed inherited
Latitude from Located asset to Coordinate. Saved all edits. Data layout inspected
at1440x900 and1100x700; no overlapping controls observed.
Separate ui-r6-scenario-windows/model/project.arcadia was constructed with the core
API from the saved native data, adding functional ports/exchange, correlated
Call/Reply, reused/inherited/recursive records and30 extra fields.53objects/5links
validate and source round-trip exactly. fixture-generator.rs retained as evidence;
temporary project example removed. Initial generator compile/load errors were
fixture-only and fixed using the bundle-aware loader; final build has no warnings.
This preparation is not native scenario authoring evidence.
## Remaining acceptance gates
Native scenario focused/path/phase/draft behavior and paging; actual saved reopen;
source3 migration diagnostics; Linux native data/scenario workflows. Closure of
material findings before R6 acceptance. Independent semantic/correctness/spec
reviews already drove fixes; final native observations may require further review.
The broader release still needs semantic reconciliation, direct scenario/process
editing, lifecycle/hosted/help work and fresh cross-disciplinary end-state review.
## Native findings and follow-up fixes (2026-09-06)
At1100x700, the53-object recursive fixture exposed32/33 nested fields in two pages.
Native Call+Reply creation stored distinct Origin/Latitude and Destination/Latitude
expressions despite their shared terminal UUID. One Undo removed the pair; Redo
restored it. Save All wrote55objects/5links; source confirms both paths and reply
correlation. Three expected checks concern the absent SA System and two unallocated
functions. The saved evidence is ui-r6-scenario-windows/model/project.arcadia.
A third-hop popup exposed a material UI defect: repeated long path prefixes hid
all terminal field names. Fix74989b6 shows the terminal name first, sorts choices,
and retains the full address as wrapping detail text.262 desktop tests, strict
Clippy and release build passed; native retest is pending. Intermediate fixed hash
E592165A6FFFAEC64B67FF0A33FB4E78571527EE3C8F2CC457666DA6C277D5CB.
Native source3 import showed8semantic defaults,2generated allocations,1unresolved
value address and left the original hash305F19D1B79F99F8D4CF627BA2E9CA6836BB7959CC0070227815937A699EAFEA unchanged.
Its old desktop Save All path requested a new file and preserved the original, but
canonical regeneration discarded comments/nesting in the upgraded copy. This is a
material desktop/source parity defect, not a core bundle migration defect. The fix
keeps the historical single-document adapter only for source1/2; source3+ follows
bundle migration and propagates its report into Model checks. A regression passes
for actual disk single-file and included-file open, no write until explicit save,
comments, lexical ownership, report, exact saved source and clean reopened state.
Full desktop/Clippy/build and native retest are in progress; do not accept R6 yet.
The old Windows native window1835476 closed normally; exec37598 exited0 with no
runtime output beyond a newline. Re-inventory before opening the rebuilt binary.
## Migration retest and bounded property browsing
f5467e5 passed263 desktop tests, strict all-target desktop Clippy and optimized
release on Windows/Linux. Frozen Windows SHA256
33D1B2B6709BBC734E265568C7F93CB86A506497DEF3F4531669E5ABF8B2F039.
Native fresh legacy.arcadia import preserved original bytes until Save All,
displayed8defaults/2allocations/1unresolved address, and retained comments and
nested fields in its text editor. Save All kept the same file and source4 wording;
reopen was clean15objects/3links/4checks without a migration report. Saved SHA256
A8C0A689FEA6CC352007ACB127DE4A45E05290830DE815385CFFBAB777255543.
Evidence: ui-r6-final-windows/migration/legacy.arcadia.
The55-object recursive scenario reopened with both qualified expressions intact.
Leaf-first labels fixed the hidden terminal names, but existing-value properties
still flattened every representative path, causing repeated indistinguishable
leaf names. The follow-up browses one record at a time, with24-choice pages and
wrapping current address/location text. Up/root/page actions change only browser
state; draft field and expression are preserved.264 desktop tests and strict
Clippy passed, including wide-record page coverage and draft preservation across
recursive browsing. Optimized build and native retest remain pending. Frozen
f5467e5 window3476204 closed normally; exec93768 had no runtime output.
+236
View File
@@ -0,0 +1,236 @@
# Direct scenario editing — bounded implementation plan
Status: implementation and automated Windows checks passed in the isolated
`codex/scenario-direct` checkout, based on ab661f2. Initial production is d1d8f0b;
independent-review fixes are in 7a66607 and e7e37d4. The focused independent
recheck reports no remaining medium/high finding. Root-owned Windows/Linux native
trials remain pending. DATA b35dede is integrated in dfd2e41 with 286 desktop
tests, strict Clippy and a fresh frozen Windows release artifact. This does not
complete the release or process/chain work.
## Decisions
Extend the existing `behavior::Scene`; retain its derived lifeline columns and
ordered occurrence rows. No model/schema/layout records are added. Lifeline and
occurrence UUIDs remain distinct from represented work and global exchanges.
Select mode selects headers, stems, messages and occurrence boxes. A selected
occurrence has a reorder handle; dragging it chooses a visible slot in its same
parent sequence. It never reparents or changes lifeline columns. A Message tool
uses the existing MessageStyle, with Call + correlated reply initially selected.
Click-click or drag between represented lifelines captures a highlighted explicit
insertion slot `(parent, before occurrence or append)` and opens the existing
typed draft with those endpoints. Exchanges are filtered by layer, viewpoint and
the core's represented-endpoint compatibility. Earlier calls retain complete
exact-slot validation, cached by document revision, owner, slot and endpoints.
Choices use search plus 24-item pages; every final creation still validates the
complete ordinary command at the captured insertion slot.
No compatible exchange produces an actionable retained draft, not a new global
exchange or a silently changed endpoint. Timer remains available as a local tool.
The canvas remains rendered while a bounded, independently scrollable form is
open. Values keep the existing qualified-path editor and lifecycle. Final creation
uses ordinary CreateElement plus SetSteps in one Batch; paired call/reply are
inserted together and undo together. Reordering uses SetSteps within the captured
parent. Existing core validation decides reply order, branch dominance, lifetime,
phase and endpoint compatibility. Failed preparation/source writes retain drafts.
Transient events capture document/scenario/revision/epoch, and the App validates
the current Behavior owner before handling them. Selection does not invalidate a
gesture within the same scenario. Escape, unfocus, context/tool change and pending
drafts cancel gestures. Scroll, bounds changes and actual window resize also
cancel the captured source/slot. Ordered raw pointer events are translated by the
actual combined inner/page Scrollable offsets. No geometry is persisted.
Execution bars derive their ends from actual correlated reply positions; separate
conditional reply branches render their corresponding spans. Calls without a
visible reply use a short explicitly open activation, not a fabricated reply.
## Acceptance checks
Production-event tests cover hit priority, click-click/drag, scroll-origin changes,
Escape/unfocus/stale contexts and exactly one result per gesture. Planner/App tests
cover middle/operand insertion, paired identities, complete SetSteps ordering,
same-parent reorder, rejected reply-before-call, unchanged global exchanges, exact
source/model Undo, draft retention and bounded exchange/call controls. Run focused
and full desktop tests, strict all-target desktop Clippy and optimized build with
the dedicated `D:/CodexBuilds/Capella2-scenario-target` target.
Root native tasks (not yet performed):
1. Draw an existing exchange again into the middle of a scenario as Call+Reply;
enter a qualified value, select both occurrences, Undo/Redo, save/reopen.
2. Insert inside an ALT/LOOP operand, reorder within it, reject a reply before its
call, and inspect correlated execution bars and preserved occurrence identities.
3. Cancel drawing after scrolling/layout change, retry, retain typed values while
navigation is blocked, and open/return from a referenced scenario. Verify at
1440x900 and 1100x700 on Windows and Linux before scoped acceptance.
## Initial automated evidence — 2026-09-06 (superseded by review fixes)
Source checkout: `D:/CodexBuilds/Capella2-scenario-direct`, production commit
`d1d8f0bbed5aa0112189138a1cc025affe26c09b`. Changes are confined to desktop
`app.rs`, `behavior.rs` and `behavior/scenario.rs`; no core model, persisted layout
or schema changes are included.
All commands used the dedicated `D:/CodexBuilds/Capella2-scenario-target` target
with `--locked --offline`:
- `cargo check -p oa-desktop --all-targets`: passed.
- `cargo test -p oa-desktop direct_`: 19 passed, including 13 new direct-scenario
tests and six existing matching tests.
- `cargo test -p oa-desktop`: final source passed all 274 tests, zero failures or
ignored tests. This includes exact source/model Undo, creation and reorder
selection, rejected stale contexts, retained failed-write drafts, bounded
contextual controls, scroll-origin cancellation and correlated activation spans.
- `cargo clippy -p oa-desktop --all-targets -- -D warnings`: passed.
- `cargo build -p oa-desktop --release`: passed.
- `git diff --check`: passed before the production commit.
Logs are under `D:/CodexBuilds/`: `Capella2-scenario-check.log`,
`Capella2-scenario-direct-tests.log`, `Capella2-scenario-desktop-final-tests.log`,
`Capella2-scenario-desktop-clippy.log` and `Capella2-scenario-desktop-release.log`.
Windows artifact:
`D:/CodexBuilds/Capella2-scenario-target/release/arcadia-studio.exe`,
20,345,856 bytes, SHA-256
`7CC6DCDB7ECC0C6B144137D31DB0BE9BC1555FEFAE6B465DAEABCF52DFE1A166`.
This artifact has not been native-tested. Widget-tree/event tests do not establish
visual usability, scrolling behavior or GPU rendering acceptance; the three native
tasks above remain required. Process/chain direct editing remains outside this
bounded implementation.
## Independent-review fixes and final automated evidence — 2026-09-06
Production commit: `7a666076bfcfd1f2258d2f5508328db5adf40825`, same isolated
checkout and dedicated target. Only the three previously owned desktop source
files changed. No core/source/schema or process/chain features were added.
The new event regressions failed on the prior implementation: a bounds shift kept
the first click's source/slot armed, and a held drag could still publish its old
slot after the bounds changed. Both now cancel. After scrolling, resizing or
clipping changes, the next click starts a fresh gesture. App tests also verify
unchanged-size notifications retain context, actual resize invalidates it, and
scrolling preserves property/source history and the pending behavior form.
Local Iced inspection established that Scrollable translates its supplied cursor
but leaves raw CursorMoved coordinates and the canvas layout unchanged. The
existing inner and outer scrollables now have a local, passive observer which
reads their actual translation through Widget::operate. Ordinary on_scroll cannot
report initial/remounted state or passive content-size clamping. The observer
forwards layout, drawing, overlays and event capture, and publishes only changed
translations or document/owner context. Reporting before child input makes a
result using an old slot stale before App processes it; reporting after input
captures wheel/scrollbar changes. The real Iced Scrollable test uses its debug
null renderer and verifies 0 → 180 wheel offset → 50 passive clamp → 180 on
expansion → 0 after remount, with no repeated messages on unchanged redraws.
The reported ALT fixture also failed before the fix: a root Call, Reply then
Action in operand one, another Reply in operand two, and a third operand without
a Reply. Derived execution geometry now splits at operand boundaries, stops each
returned operand at its own correlated Reply, leaves its following Action outside
that segment, and marks the reply-free operand open. It does not imply chronology
between alternative operands or add persistent execution records.
Picker measurement on the same Windows debug target:
| Probe | Before | After |
| --- | --- | --- |
| 500 compatible exchange choices | 7.87 s | 1.46 ms |
| Same choices after one name edit | 8.14 s | 1.48 ms |
| 100 exact-slot earlier calls, initial validation | not measured | 595 ms |
| Those calls after name/value/search/page edits | not measured | 34–39 µs |
The exchange list no longer clones and validates the entire project per candidate.
Final Apply still rejects a Delete inserted before a later Call on that lifeline;
the new regression confirms appending that same Delete succeeds. Reply choices
retain exact validation, invalidate for changed slot/document/revision/endpoints,
and do not lose their cached eligibility while expressions are being typed.
These are bounded picker measurements, not complete native-frame timings.
Final checks (`--locked --offline`): 27 focused matching tests passed; all 282
desktop tests passed, zero failures/ignored tests; strict desktop all-target
Clippy passed; optimized desktop build passed; git diff --check passed. Clippy
requested one test-only nested-if cleanup, which was applied before the final
clean Clippy/build.
Logs under `D:/CodexBuilds/`:
`Capella2-scenario-review-before-tests.log`, `Capella2-scenario-alt-before.log`,
`Capella2-scenario-scroll-observer-tests.log`,
`Capella2-scenario-review-desktop-tests.log`,
`Capella2-scenario-review-clippy.log`, `Capella2-scenario-review-release.log`.
Current Windows binary:
`D:/CodexBuilds/Capella2-scenario-target/release/arcadia-studio.exe`,
20,349,952 bytes, SHA-256
`1AE729097F267DE596AE0970D3A62AE06C0C89CE2FDE70DF980AD785A0E0A8E6`.
It supersedes the initial artifact above. Native acceptance is still pending.
Root should repeat the three primary native tasks, including inner/outer wheel
and scrollbar cancellation at a middle slot, resize/remount/clamp recovery, and
the three-operand ALT fixture. The broader release objective remains incomplete.
## Final passive-scroll save correction — 2026-09-06
Production `e7e37d485d842731d270781bd4e60a323fe4cdba` addresses the focused review's remaining App lifecycle
finding. During an async save, Iced can change its scroll position while App's busy
gate rejects ordinary messages. The observer had already recorded its report,
so dropping that notice left App's offset stale after save completion. The new
regression failed before the fix: App retained offset (0, 0) instead of (25, 140).
App now dispatches only the passive Scrolled event before interaction gates. Its
existing handler still validates document, scenario and revision. Mutation input
remains gated. The test checks current offsets and cancellation epoch while busy,
rejects an old-document observation, delivers a successful Saved receipt, then
uses production Canvas pointer events to create the intended sender/receiver/slot
draft without a model or source-history mutation.
The independent reviewer rechecked this exact production change and regression;
no remaining medium/high finding was reported in the focused review. Final full
desktop tests pass 283/283, strict all-target desktop Clippy passes, optimized
desktop build passes, and git diff --check passes. Logs under D:/CodexBuilds are
`Capella2-scenario-busy-scroll-before.log`,
`Capella2-scenario-busy-scroll-after.log`,
`Capella2-scenario-busy-scroll-desktop-tests.log`,
`Capella2-scenario-busy-scroll-clippy.log` and
`Capella2-scenario-busy-scroll-release.log`.
Latest Windows artifact, superseding both builds above:
`D:/CodexBuilds/Capella2-scenario-target/release/arcadia-studio.exe`,
20,349,952 bytes, SHA-256 `CEEB8E277DFC47C0D55DA43F903FCA45AFBF902AFA418B3924375910D699373A`.
Native acceptance is pending. Include scrolling while saving in the root's native
recovery task before accepting the direct scenario milestone.
## Combined DATA/scenario integration — 2026-09-06
Merge `dfd2e41259e73ef10a4e29a426569c08eaae215f` integrates `codex/data-contract-integration` at
`b35dede75fc8778ea695b9b2c023cfa2a1e49e09` into the isolated scenario branch.
The merge applied without conflicts and needed no manual code resolutions or new
semantic design. Direct scenario geometry/planners/tests and the existing
scenario evidence were unchanged by the merge. App's early passive-scroll handler
remains intact alongside DATA's source migration and property-browser handling.
The shared field-path API remains compatible: compact leaf-first labels and
deterministic sorting now feed the existing bounded scenario value picker, while
full qualified labels remain available for detail text.
Combined verification on the dedicated scenario target: all 286 desktop tests
passed, no failures or ignored tests; strict desktop all-target Clippy passed;
optimized desktop build passed; staged diff --check passed. All commands used
`--locked --offline`. DATA's three new regressions are present together with the
283-test scenario suite, including source3 save/reopen and direct gesture/save
lifecycle coverage. No DATA checkout, Linux checkout or native application was
modified or operated in this integration task.
Frozen artifact directory: `D:/CodexBuilds/Capella2-release1-01a06e2e/ui-scenario-data-windows-dfd2e41`.
It contains `arcadia-studio.exe`, the three combined verification logs and a
`manifest.json` identifying the source checkout, merge commit, target and hash.
The copied binary hash was checked against the release target binary.
Binary size: 20,411,392 bytes. SHA-256: `8BC031CF6124859D31082BB0FFB0335C406D54E12ECAD8493CE50A23923E5589`.
Root reports DATA b35dede's Windows native trial and independent focused review
passed. This does not transfer native acceptance to the combined direct-scenario
workflow. Its Windows/Linux native tasks remain pending, including scroll/resize
cancellation, middle/operand insertion, execution spans and save recovery. Root
owns final integration into main and native acceptance; process editing remains
outside this task.