Record Text alias rename proposal and updated navigation acceptance scope

This commit is contained in:
2026-09-11 12:08:32 -07:00
parent 2dedde29e9
commit fb16c4200f
3 changed files with 145 additions and 5 deletions
@@ -0,0 +1,63 @@
# Text-caret alias rename: independent transaction critique
Codex-9e8r6t /root/release_audit, 2026-09-11T18:34:04.889481+00:00. Read Gitea #9 and current implementation independently of the design worker. This is source-traced adversarial design analysis: no implementation change, executable experiment, test, compiler, GUI, server or Linux action. Counterexamples below falsify possible adapters; they are not claims that an unimplemented Text refactor already fails.
## Existing guarantees worth preserving
`Document::execute` prepares a typed command on a cloned Project, runs the identity-aware source updater, then commits only the completed result (`document.rs:301`). `prepare_command` carries complete surviving UUID correspondence (`session.rs:170`); SetAlias edits only the identified record header (`session.rs:607`, `816`). `Project::identities` includes project, elements, relations, diagrams, node placements, artifact links and traces (`model.rs:1726`), so element-only lookup is insufficient. Alias syntax is ASCII identifier syntax with exact reserved-word exclusion (`model.rs:1704`); do not trim, lowercase, or accept an engineering-name Unicode policy as alias validation.
The updater recompiles/rechecks public source/model/index inputs, preserves ownership and syntax trivia, edits actual reference alias tokens even across intervening comments, and recompiles the result to require exact requested Project equality (`source_project.rs:780`, `1215`, `1003`). Use it across the complete accepted bundle. Existing lexical navigation is only a locator: its result-row budget and partial/detached provenance are not an edit plan (`source_navigation/query.rs:832`, `856`, `882`, `937`).
## Concrete counterexamples and required boundaries
1. **Preview by execute-then-Undo mutates history.** Start with a real Redo branch, call the live Document execute to obtain preview bytes, then Undo. Commit clears the previous future and may evict old history for the 100-entry/32MiB budget (`document.rs:378`); Undo cannot restore discarded history. Preview must work on the captured accepted snapshot, never temporarily commit to the live Document. `Document::from_project` is also the wrong preview base because it formats a new bundle (`document.rs:116`); it loses authored file layout/comments before the alleged rename.
2. **A self-consistent preview can overwrite later engineering edits.** Preview rename A→B from accepted snapshot S. Accept another valid edit, producing S2. `apply_compiled(preview_S_B)` recompiles and accepts that old valid bundle; it checks internal consistency, not that S is still current (`document.rs:324`). The S2 edit disappears from current state. Bind the preview to the actual document instance/session, accepted Arc snapshot and revision, source workspace session/revision and full accepted/source input closure. Recheck at confirm. A project UUID alone is not a document identity: copies/reopened projects can deliberately retain it. Revision alone also does not distinguish a new Document starting at zero.
3. **Public compiled data is not proof of the reviewed command.** Alter only `decoded.project` or index: existing `apply_compiled` correctly rejects. But compile a different valid bundle that also renames an engineering name or removes an unrelated record, and it passes internal consistency. Likewise `PreparedCommand` has public mutable fields (`session.rs:103`). These are data, not a sealed rename authorization. Do not let a confirm message carry arbitrary compiled data or a general command. Keep private immutable intent `(target UUID, captured old alias, proposed exact alias)` plus the preview's complete proposed result; confirm by token owned by the current form.
4. **Unchanged accepted model does not imply unchanged editor input.** Preview from accepted S, then edit a different included file, a detached buffer, a property field or a pending behavior form. S and Document revision can remain unchanged. Generic App execution later calls `sync`/`sync_source`, replacing editor buffers and refreshing properties (`app.rs:863`, `709`, `649`). Refuse at entry and confirm when source is unapplied, any required draft is pending, scope is incomplete/detached/ambiguous, or the captured workspace/file set/text changed. Do not call generic `apply()` to make these conditions disappear. Include unchanged dependencies in stale checking: editing only an include or an unaffected module must invalidate the reviewed closure. Ordinary later Save retains existing disk conflict checks; preview/confirm is not a fresh disk read or a disk transaction.
5. **No-op must not use App's generic successful-command refresh.** SetAlias(id, same alias) returns `Some(id)` even when Document commit does nothing. App::execute interprets this as success and calls sync; Workspace::accept rebuilds Buffers (`source_workspace.rs:331`), discarding their text Undo/Redo stacks. Counterexample: make a temporary source edit, Undo it to accepted bytes (source clean, text Redo present), then confirm an unchanged alias. Model history remains unchanged but text Redo is lost. Detect exact unchanged alias/unchanged complete candidate and report no change without invoking the generic refresh. Preserve selection, caret, raw snapshots, dirty/saved markers and both histories. Existing Document no-op tests (`document.rs:696`) do not cover that App seam.
6. **Preview-to-confirm substitution and replay.** Preview A→B, edit the input to C, then deliver an old queued Confirm; recomputing from the live input applies C although B was reviewed. Alternatively Close/reopen a form on the same target, then replay an old confirm. Invalidate the prepared preview on every raw name edit; confirm carries a fresh preview token, and uses only its immutable captured intent. Consume/clear it on successful commit or no-op; a second click cannot create a second history entry. Failure/stale refusal retains the user's proposed name for repair while marking the old preview non-confirmable. An accepted edit followed by Undo back to byte-identical S should still invalidate the earlier review via revision/token policy; do not silently revive stale approval.
7. **CLI behavior cannot be copied as Text-preview admission.** CLI prepare explicitly compiles raw disk input, stabilizes missing UUIDs and upgrades legacy source; its rename preview may contain those changes and says later invocations may allocate different IDs (`oa-cli/lib.rs:287`, `306`, `355`; test `source_cli.rs:302`). Text #9 instead starts from an already accepted snapshot and refuses unapplied input. Preview must not secretly turn ID-less or old-schema editor text into an accepted model. A valid accepted Document may already contain explicit IDs or migration changes relative to disk; those are the baseline, not extra edits caused by alias rename. Its existing unsaved flag must survive cancel/no-op.
8. **Global identities and bounded lexical rows.** A declaration's alias may belong to a node, diagram, project, relation, trace or artifact, not an Element. A same-named alias in detached text or a reference with partial scope does not authorize choosing the first accepted object. Require a unique current accepted declaration and verify its UUID, alias and exact location against the accepted model/index. Independently test a target with more uses than the navigation row cap: all true source references must change, even though only 200 or fewer rows were displayed. Comments, string/prose occurrences, other aliases and historical UUID literals stay exact. Count changed files/preview changes from the complete before/after bundles, not the lexical list; do not label lexical totals as typed-reference coverage.
## Transaction alternatives
| Approach | What makes it sound | Main tradeoff / falsifier |
| --- | --- | --- |
| Recompute typed SetAlias on confirm | Private immutable intent, exact base/closure/token guards, preview produced with the same core preparation/updater, confirm calls Document::execute on that unchanged base. Stable accepted IDs and SetAlias introduce no creation identities. Verify complete preview result equals actual result in tests. | Repeats bounded compile/update work. Using live text or skipping base guards falsifies correspondence. Avoid a separate live commit merely to compare results. |
| Adopt public `apply_compiled` preview | Must additionally prove unchanged base and exact SetAlias correspondence; recompilation alone is insufficient. Keep candidate private and recheck full proposed bundle/model/index. | Convenient reuse, but general source replacement can admit a consistent unrelated change or stale old snapshot. Least attractive default for this narrow command. |
| Narrow Document-owned prepared alias | Private constructor captures base Arc/revision, exact SetAlias intent and validated complete compiled result. Read-only preview accessors; consuming confirm validates base and commits exactly that result. App separately checks source/form/session closure. | Avoids repeated updater work but adds a transaction API and lifecycle to maintain. Keep it alias-specific; do not invent a generic trusted-compiled capability. Public mutable prepared fields defeat the benefit. |
**Recommended initial implementation:** a private App-owned preview of the exact typed SetAlias intent and complete output, with recompute-on-confirm through existing Document::execute after strict unchanged-base checks. This is adequate for deterministic alias-only edits on already stabilized accepted input and adds the least new authority. Handle no-op before generic App refresh. If measured large-model preview/confirm latency makes recomputation material, move the same bounded contract into an opaque Document-owned prepared alias; do not choose an unchecked public compiled shortcut. Neither option needs a new schema, evaluator, file loader or generic capability system.
## Independent evaluator
Prepare literal expected files with explicit fixed UUIDs, mixed UTF-8 names/paths and CRLF, comments between `@` and alias, strings containing `@old`, nested declarations, multiple modules and an unchanged include dependency. Build the expected Project by changing precisely one known header alias; assert equality of every remaining field/UUID/relation/view/placement and exact before/after file inventory and bytes. Define expected changed-file membership and complete hunk contents independently of the producer updater. One changed alias token does not imply one changed file or one reference.
- Preview and cancel: exact Document snapshot/revision/saved state/model Undo/Redo, Workspace recovery bytes/cursors/text Undo/Redo, property/behavior raw drafts, filesystem bytes unchanged. Rendering/scrolling preview does nothing.
- Confirm: exactly one model/source commit; complete result equals the displayed preview. One Undo restores exact S; one Redo restores exact renamed result. Save/reopen reproduces that result, but persisted Undo history is not promised.
- No-op with both model Redo and clean-buffer text Redo available: no new revision, history entry, source rewrite, dirty change, cursor move or sync-induced loss. Invalid/reserved/duplicate/whitespace alias likewise preserves all state and exact proposed input.
- Stale: accepted unrelated edit; comment-only edit in an unchanged dependency; include/file-set change; dirty detached file; property/behavior draft; same-UUID/same-path new document; Save As; recovery; accepted change→Undo; name changed after preview; Close/reopen and old confirm. Every refusal precedes mutation; no implicit Apply.
- Counterfeit data controls: inconsistent public compiled model/index must refuse; a self-consistent unrelated compiled candidate must never enter through alias confirmation. Mutating exposed preview display data, if any, must not change the stored command or adopted bytes.
- Global record classes, UUID/alias collision and more-than-row-limit usage fixture. Refuse partial/unapplied/ambiguous/detached input; do not infer all-project coverage from a bounded result array.
- Actual Windows UI: focused Text-caret entry, exact target identity/context, keyboard and mouse name/preview/confirm/cancel, stale status, long changed-file list at minimum size, first Escape/modal priority, one Undo/Redo, ordinary save/reopen and full independent source/model oracle. Existing core/CLI evidence is supporting evidence only; no such Text UI pass exists yet.
## Inspected raw source identity
| File | SHA-256 |
| --- | --- |
| `crates/oa-desktop/src/document.rs` | `2ecc2cd496d4a83fd7a32b9dc68e49f0f1cb31a06263dfd6939b4764da5b73e2` |
| `crates/oa-desktop/src/app.rs` | `370c2ce1b58f5aad4cfbbbae4a774670a20c6f4f5f1a77305daf8ce221f80dbe` |
| `crates/oa-desktop/src/source_workspace.rs` | `388c71f63829c0c76526a1700ebf5f240c86c467a65aae4fca4cd6c6a88c468b` |
| `crates/oa-desktop/src/source_navigation/query.rs` | `aa8e8e9d446b57c30520c4b3ca1ae9fe524cecb2f4f4f4b229d930ea0377ecca` |
| `crates/oa-core/src/session.rs` | `1eb77b329b2986fc6f5dc0171e77fd860375cdfd8c769a1367b445d6c49ebe00` |
| `crates/oa-core/src/source_project.rs` | `1394370bcc5908873b830c2c11312e6e1611af41f46c4f43ca0a6bcb713bb466` |
| `crates/oa-core/src/model.rs` | `4585c895c64c4a183941c92cf047a9e52ff0cbe41971e16e78d229ea9dc101fb` |
| `crates/oa-cli/src/lib.rs` | `97bebab63b4a611a1d36ad0c20fe452cbe1985fe3a6fbd39c954603da11410fc` |
| `crates/oa-cli/tests/source_cli.rs` | `a3a1b856c7a9fd3d80bc8074150a24fbb0ef0e9ddf736c7cea036725fc3dc2b8` |