Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b9932236a | ||
|
|
afd81af1f1 | ||
|
|
35d9eab302 | ||
|
|
22e974da35 | ||
|
|
bf3f2f69a2 | ||
|
|
75b8815a83 | ||
|
|
b2127cbe90 | ||
|
|
1bf8ebca96 | ||
|
|
dfd2e41259 | ||
|
|
05a84b217d | ||
|
|
e7e37d485d | ||
|
|
b35dede75f | ||
|
|
5fa9c905c8 | ||
|
|
7a666076bf | ||
|
|
f5467e50c5 | ||
|
|
74989b6f25 | ||
|
|
524dabb9c3 | ||
|
|
d1d8f0bbed | ||
|
|
0edadeebdb | ||
|
|
93e3137342 |
@@ -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
@@ -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 -->
|
||||
@@ -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");
|
||||
|
||||
+1434
-53
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) = ¤t_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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user