diff --git a/crates/oa-core/src/session.rs b/crates/oa-core/src/session.rs index b679ac13..be06131e 100644 --- a/crates/oa-core/src/session.rs +++ b/crates/oa-core/src/session.rs @@ -85,6 +85,31 @@ pub struct Session { saved: Project, revision: u64, } + +/// One validated command result, ready for an owning editor or service to adopt. +/// Preparation never mutates the input and allocates creation identities once. +/// A source editor can validate its corresponding document edits before either +/// representation becomes visible. +pub struct PreparedCommand { + pub project: Project, + pub selected: Option, +} + +pub fn prepare_command( + project: &Project, + command: &Command, +) -> Result { + let mut candidate = project.clone(); + let mut pending_deletions = BTreeSet::new(); + let selected = apply_command(&mut candidate, command, 0, &mut pending_deletions)?; + crate::references::finish_deletions(&mut candidate, &pending_deletions); + validate_candidate(&candidate)?; + Ok(PreparedCommand { + project: candidate, + selected, + }) +} + impl Session { pub fn try_new(project: Project) -> Result { validate_candidate(&project)?; @@ -143,11 +168,10 @@ impl Session { Ok(()) } pub fn execute(&mut self, command: Command) -> Result, ModelError> { - let mut candidate = self.project.clone(); - let mut pending_deletions = BTreeSet::new(); - let selected = apply_command(&mut candidate, &command, 0, &mut pending_deletions)?; - crate::references::finish_deletions(&mut candidate, &pending_deletions); - validate_candidate(&candidate)?; + let PreparedCommand { + project: candidate, + selected, + } = prepare_command(&self.project, &command)?; if candidate != self.project { self.past .push(std::mem::replace(&mut self.project, candidate)); diff --git a/harness/DECISIONS.md b/harness/DECISIONS.md index 6f718c6b..ec639e9c 100644 --- a/harness/DECISIONS.md +++ b/harness/DECISIONS.md @@ -29,3 +29,4 @@ - Source/schema3 is now implemented. The original V2 comment-normalization decision is superseded for the first-release source-project work: validated bundle updates preserve concrete tokens/comments/whitespace, while explicit canonical export remains available. Desktop bundle editing and experimental nested syntax are separate acceptance gates; neither is implied by the pure-core updater. - Connection routing is per diagram, keyed by an existing relationship/trace ID. Store ordered pinned points and relative label placement; derive orthogonal elbows in a pure Rust core shared by canvas, picking, Fit and SVG. Typed port anchors never move to improve a route. Bounded automatic routing provides an initial path; manual pins express engineering layout intent. Collapse temporarily suspends authored routing when endpoints are promoted, preserving it for expansion. One pointer gesture commits one atomic command; stale/invalid-draft gestures are rejected. +- Source workspace integration uses one accepted history of compiled source/model snapshots. The existing semantic Session remains for model-only consumers; a shared `prepare_command` supplies validated candidates and creation selection without committing or generating IDs twice. Desktop source writeback must succeed before adoption. Comment-only accepted edits are real history entries. Drafts and disk baselines stay separate; see release-source-integration.md for the implementation and acceptance contract. diff --git a/harness/release-source-integration.md b/harness/release-source-integration.md new file mode 100644 index 00000000..1ed28dc5 --- /dev/null +++ b/harness/release-source-integration.md @@ -0,0 +1,86 @@ +# Source workspace integration contract + +Status: implementation in progress after the accepted routing checkpoint. This +is the next vertical slice of the first release, not release completion. + +## Observable outcome + +A system engineer opens an entry `.arcadia` file and edits its included files +independently. Switching files retains invalid drafts and cursor positions. +Located project diagnostics navigate across files. Apply adopts the entire +validated source/model pair atomically. Graphical edits preserve comments and +file ownership. Undo/Redo restores exact accepted source, including comment-only +edits. Save All writes the project with conflict detection and recoverable disk +failure handling; reopen reproduces the accepted model and source. + +## Architecture and sequence + +1. Add `prepare_command(project, command)` to the shared semantic command engine. + It returns a validated candidate and selected identity without mutating input + or executing creation twice. Existing semantic Session uses the same helper. +2. Desktop `Document` owns one history of accepted `CompiledSourceProject` + snapshots. Command preparation followed by `update_bundle` succeeds before + adoption. Source-only accepted edits advance revision and history. Separate + draft history remains an editor concern; do not synchronize two accepted + model/source undo stacks. +3. Keep filesystem code outside pure `source_project`. Discover includes with + `inspect_includes`, enforce aggregate budgets and portable/contained physical + paths, retain exact original disk bytes before identity stabilization. +4. Integrate file navigation, full-bundle asynchronous lint, Apply, Open and Save + into the existing Iced application. Results carry document identity and draft + generation. The active text control renders one file; inactive drafts persist. +5. Extend production syntax through the same compiler/index/updater, separately + from this integration. Never copy the experimental lowering/writer wholesale. +6. Verify real native two-file editing, failure repair, graphical rename, exact + Undo, Save All and reopen; obtain independent correctness and UX review. + +## Failure and persistence decisions + +- Dirty text in any file blocks graphical mutation until explicitly applied or + reverted. A failed compile/writeback leaves model, source, revision and history + unchanged. Diagnostics retain original per-file spans and related locations. +- Successful lint is advisory: only explicit Apply adopts inserted identities. +- Syntax/missing-include errors retain loaded editable documents. Removing an + include detaches its buffer; it does not delete a file. Compile reachable files + while retaining detached dirty content visibly and guarding close/discard. +- Check all dependency baselines before saving, including unchanged files. + Stage exact bytes, keep recovery information, replace the entry last and roll + back ordinary partial failure. Interrupted recovery must not overwrite a newly + observed external edit. This is recoverability, not filesystem-wide atomic + visibility or compare-and-swap with other editors. +- Source Save As retains relative file layout and reviews destination collisions. + JSON remains whole-model interchange and cannot erase source layout/history. + +## Production syntax direction + +Retain flat syntax. Nested elements express semantic parent only; allocation +stays explicit. Contextual input/output shorthand creates real SA function ports, +with item lists and an optional ordinary field body. FE shorthand references real +ports and infers redundant function endpoints after resolution. Ordered steps +blocks cover existing scenario/process/chain/Branch semantics. Typed value bodies +remain ordinary fields; includes stay document-level. No new global reserved +aliases. The parser must expose real spans and lexical ownership, never fake +spans for inferred values. Structural edits compose around the existing +UUID-aware scalar/list updater. + +Contiguous leading and same-line trailing comments travel with their declaration; +blank-separated comments stay in the containing gap. Deleted syntax leaves its +comments in the old body with separation so they cannot become the next record's +documentation. Preserve authored flat placement; prefer owner-local placement +for new children. Equal explicit parent and nesting may coexist; contradictions +fail. Shorthand-derived contracts/endpoints have one definition. Valid edits that +outgrow shorthand expand that declaration locally instead of rejecting the edit. + +## Acceptance evidence required + +- Creation selects the same UUID persisted in source; rejection is fully atomic. +- Comment-only Apply/Undo/Redo and history branching restore exact bytes. +- Two-file errors and stale lint/discovery cannot change newer drafts or models. +- IDs stabilize once while disk conflict baselines retain original bytes. +- Item rationale comments follow UUIDs through reorder and alias swaps. +- Nested create/delete/reparent and occurrence reorder/move preserve identity, + comments, allocations and unrelated files; empty steps blocks remain editable. +- External edits/deletions/new-file collisions reject Save All before loss; + injected partial failures and interrupted recovery preserve coherent content. +- Default/minimum-size native editing and save/reopen are observed, not inferred + from compilation. Windows/Linux builds and focused regressions pass.