Files
ARCADIA-Studio/experiments/source-authoring/WRITEBACK.md

70 lines
9.2 KiB
Markdown

# Bounded writeback experiment
Status: executable second gate, 2026-09-05. Production syntax remains undecided. The first gate is preserved in commit `c5788fc`; this gate changes only `experiments/source-authoring/`.
## Question
Can complete model edits write back to the tested B constructs and ordinary A fields while preserving comments, authored order, nested/concise syntax and multi-file organization? Or does the apparent simplicity of one-way lowering require destructive canonical replacement when the model changes?
Competing expectations: (1) scalar token spans and whole owned-declaration movement suffice for these workflows; (2) correct writeback requires broad syntax-tree regeneration; (3) some source organizations are ambiguous and need a declared editing policy rather than an inferred one.
## Method and implementation
`writeback::writeback(&SourceBundle, &Project)` accepts the actual complete requested model, not a hand-written textual edit recipe. It compiles the existing bundle, validates the requested candidate with core source3 validation, derives model record changes, applies bounded span edits to a cloned bundle, and recompiles the result. It returns a replacement only if that complete model equals the requested candidate. No caller state or disk files change on failure.
The index records declaration/header/value spans, B shorthand endpoints, lexical owners and steps blocks. It does not implement a second semantic model or a general concrete syntax tree. Matching object fields retain their authored order; changed scalar tokens and supported list additions retain surrounding comments/whitespace. Alias renames form one simultaneous lexical phase, followed by re-indexing; alias cycles are supported. Names/comments/ordinary strings are never interpreted as references.
Semantic ownership movement differs from source organization. An explicitly parented A declaration changes its parent field in place. A nested B declaration moves to its new semantic owner's body, including nested ports and attached comments. Allocation relations are unchanged unless the candidate explicitly changes them. Ordered B occurrence blocks reorder by stable UUID; newly created occurrences retain the candidate's supplied identity and use existing explicit-field bodies inside the steps block.
Tests use the three first-gate models, enriched with inline/leading comments, custom field order, equivalent noncanonical string escaping, an independent function root, a second item type, and a separate diagram document. They operate on explicit source bundles. The geometry and combined reparent/name tests use real core `Command::MoveNode` / `Command::EditElement` transactions to produce the candidate.
## Evidence actually obtained
**23 tests passed: 10 first-gate tests plus 13 writeback tests.** Strict all-target Clippy passes. The real CLI ran a core Rename command and printed the nested B source with only the requested name changed.
Every successful writeback test verifies complete-model equality, preservation of all input comment token text, caller-input immutability and a byte-identical second writeback of the same candidate.
| Model edit | Observed writeback and locality |
|---|---|
| Owner display-name change | Exactly the quoted name changes; only its declaration file changes. Equivalent Unicode escaping in another file remains byte-identical. |
| Owner alias change / simultaneous alias swap | Definition/reference tokens change across the expected two files; comments, prose strings and implicit ownership retain their meaning and placement. |
| Unused function port Output → Input and another declared item | `output` changes to `input`, the item is appended, and the inline rationale remains beside the original item. No raw port expansion; one file changes. |
| Existing function input accepts another declared item | Port shorthand changes; its functional-exchange line remains byte-identical. |
| Component port orientation Out → InOut | Only the orientation atom changes. The author's reversed field order and nested comment remain byte-identical. |
| Repeated occurrence moves before the Call/Reply pair | Its identity and its leading/internal rationale move together; `steps { ... }` remains; one behavior file changes. |
| Another occurrence is appended | One new block uses its supplied identity; existing blocks remain byte-identical; no redundant parent/membership list appears. |
| Function reparented into an independent aggregate | Its complete block, nested ports and attached comments move across exactly two files. Explicit work/port/exchange allocations and the view file are unchanged. |
| Reparent and display-name change in one core transaction | The scalar edit is absorbed into the moved subtree; the diagram remains byte-identical. |
| Diagram placement move | Only two numeric value tokens change in the view file; custom x/y field order and comments remain untouched. |
| A-format activity parent changes | Only the explicit parent value changes; the declaration stays in its authored file/location. |
| Invalid owner or ambiguous unattached group comment during reorder | An error returns without mutating source. There is no fallback to canonical replacement. |
An initial real regression was found: canonical parent references changed during an alias rename, and the writer incorrectly treated that as an ownership move, reordering sibling ports. The fix separates simultaneous alias renaming from semantic UUID changes. The exact minimal-change assertion and an alias-swap test now prevent this regression.
## Explicit boundaries and policy questions
- A contiguous leading `//` comment at the declaration's indentation belongs to that declaration for structural movement. Internal comments and an inline closing-line comment travel with it. A blank-separated group comment between ordered blocks has no inferred owner; reordering is rejected. This is an experimental policy to review, not a claim that all existing source comments have obvious ownership.
- Whole-line declaration boundaries are required for structural moves. Scalar edits still work in compact one-line A bodies. Simultaneous nested ownership moves, moving existing occurrences between steps blocks, semantic deletions, and creating records other than occurrences are outside this gate.
- Matching object fields and tested scalar/list edits work. Arbitrary object-key insertion/removal or commented list restructuring can be rejected. A datatype/variant migration requiring different fields is not silently translated into prose-destroying replacement.
- New text currently uses LF and four-space indentation. Existing source bytes are preserved outside edits; mixed line-ending/style behavior is not yet a production editing policy. Large-model performance, branch/fragment/state authoring, AST recovery from invalid B drafts and detailed B diagnostic mapping remain untested.
- The experiment does not prove visual usability, HTTP behavior, local multi-file persistence, concurrent external-edit handling or editor Undo behavior. Recreating an earlier model need not recover the exact earlier lexical position of an unordered declaration; exact source Undo should retain document history, not attempt to infer it from model semantics.
## Conclusion and recommendation
Expectation 1 holds for the tested edits: whole-model graphical-command results can preserve the tested B syntax without canonicalizing whole declarations. Expectation 3 also holds: comment ownership and structural movement need explicit rules. This falsifies the claim that destructive regeneration is inherently required, but does not establish universal lossless editing.
The gate supports carrying ownership nesting, explicit function-port/FE shorthand and ordered occurrence blocks into a production design review. Preserve A's bundle/admission/identity foundation. Use the production parser's source spans and a shared value-editing facility rather than copying this experiment's separate indexing pass and delta writer into a parallel language subsystem. The prototype's additional indexing/editing code is evidence of real implementation cost, not a reason to hide that cost under the word "sugar".
Before freezing the grammar, independently review (a) comment attachment and reorder rules; (b) behavior when an authored aggregate contains multiple semantic owners; (c) invalid-draft preservation and exact Undo; (d) branch/fragment/state cases. Revisit the proposed constructs if these require implicit allocation, unstable identity or frequent whole-file rewrites. Do not expand into namespaces or broader SysML constructs merely because this bounded gate passed.
## Reproduce from the repository root
```powershell
$env:CARGO_TARGET_DIR='D:/CodexBuilds/Capella2-source-authoring-experiment'
cargo test --manifest-path experiments/source-authoring/Cargo.toml --locked -- --nocapture
cargo clippy --manifest-path experiments/source-authoring/Cargo.toml --locked --all-targets -- -D warnings
cargo run --manifest-path experiments/source-authoring/Cargo.toml --locked -- --rename experiments/source-authoring/fixtures/sa-b.arcadia manage 'Manage emergency reporting'
```
The CLI prints the result and does not overwrite the fixture. Includes require the library's explicit bundle API. Windows Rust 1.98.1 was used; no Linux/native-GUI/MSRV claim is made for this isolated experiment.