fix(scenario): restore focus only to the current parent form

This commit is contained in:
Codex
2026-09-11 08:07:41 -07:00
parent 8d55d2d2eb
commit 8895dd2e0f
12 changed files with 1126 additions and 60 deletions
@@ -0,0 +1,39 @@
# Scenario parent focus correction — source handoff
Codex-956atf `/root/scenario_focus_fix`, 2026-09-11, root checkout at supplied base `8d55d2d`. Implements the two source-derived findings in the frozen `harness/release-chooser-contextual-integration-review-20260911.md`. That earlier review is unchanged. Coordination: Gitea issue 1 comment 292; the isolated canvas worker retains its own App/workspace changes.
## Changes
- I1: `State::form_mount` is a separate presentation identity. The first mount uses the new session token; successful source Back, Use and Escape create another mount/focus intent without changing `form_session`, invocation, source identities, raw parent fields or model history. `form_mount()` is absent while the source child owns the editor. The production parent sensor reports the mount token; its editor scroll identity remains the form session.
- I2: `App::view` wraps the complete current tree in a small root ownership marker. The live owner includes document session, semantic owner, document revision, form session and parent mount, and is absent for source children, busy state, Review, hosted UI and other App modal owners. The exact name widget ID contains the same stamp. A delayed operation first collects the live marker and mounted exact target without modifying focus. Only a match produces the apply traversal; it rechecks the live marker before touching focus or ancestor scroll offsets. An absent target produces no apply traversal and leaves other inputs alone. Geometry epoch changes do not change the parent identity.
- The focus operation reveals its exact field through ancestor scrollables. Existing scenario canvas pending-marker reveal, parent editor scroll key, and source-versus-parent branches remain intact.
## Prepared regression gates — not executed
All five tests below are in `crates/oa-desktop/src/behavior/scenario/workspace/tests.rs`, under filter `scenario_form_focus`. Four use the actual production `App::view` at 1100×700 with `iced::Renderer` through its headless API; a missing backend is an explicit test failure, never a silent skip.
1. `scenario_form_focus_targets_the_name_and_rejects_stale_sessions_or_modals`: existing stale-dispatch coverage now executes current focus against the production root and checks exclusive Name focus, unchanged raw draft/source/history and one-shot delivery. Viewport epoch changes remain accepted. The scroll gate requires an actually overflowing parent with measured initial offset greater than 64, then requires the Name's effective bounds to lie within the measured parent viewport after focus. This replaces the first, potentially vacuous final-offset assertion identified by the independent reviewer.
2. `scenario_form_focus_returns_from_back_use_and_escape_after_initial_focus_is_consumed`: consume actual initial sensor/task; mount and focus actual source Name; return by Back, valid Use or Escape; execute the newly mounted production parent's sensor task. Checks stable semantic/raw identities and form session, changed mount, correct parent focus, unchanged recovery after focus, identical accepted Project/source bundle and untouched Undo/Redo.
3. `scenario_form_focus_saved_action_is_inert_on_replacement_child_review_and_changed_context`: obtain S1's real widget Action before changing state. Delay it across S2, source child, real Review candidate input, changed document session/revision/owner, same-session child return to a new mount, and hosted UI. Operate it on the newly mounted production tree; require no chained mutation, unchanged current focus/raw/source/history. Child and Review cases explicitly focus their own mounted inputs.
4. `scenario_form_focus_missing_exact_target_leaves_other_inputs_untouched`: matching root owner with absent target must leave a separate focused input untouched.
5. `scenario_form_focus_chained_apply_rechecks_live_review_owner`: execute current Collect, retain its chained Apply, open and focus a real Review candidate input, then execute the retained Apply. Requires unchanged modal focus, raw draft/source/history and no further chained mutation.
The async action helper deliberately does not poll task completion while retaining its widget operation: that operation owns the task channel until drop. It executes any chained operation against the current production tree.
## Validation and integration
Only source inspection and `git diff --check` were performed by this worker; the whitespace check passed before handoff. No Cargo, rustc, native UI, install, formatting, commit or release was run. Source still requires root's scoped rustfmt, compiler, `scenario_form_focus`, complete workspace tests, contextual-source tests and relevant Clippy gates. The headless renderer/backend requirement and actual minimum-size/native focus timing remain explicit runtime gates. Independent source review has been requested separately and must record its own verdict.
Owned implementation paths: `crates/oa-desktop/src/app.rs`, `behavior/scenario.rs`, `behavior/scenario/workspace.rs`, `behavior/scenario/workspace/tests.rs`, and new `behavior/scenario/focus.rs`. The last four paths are relative to `crates/oa-desktop/src`. Only this new report was written under `harness`; shared ledgers and the frozen prior review were not edited by this worker.
When merging the isolated canvas candidate, preserve the new outer App marker and `scenario_focus_owner`, guarded FormShown handler, scenario mount/state methods, generated exact Name ID and parent sensor key/payload. The canvas worker changes the surrounding workspace host and its old layout tests: union these five focus regressions and helpers instead of replacing the entire test file. Root owns the combined freeze, validation and any versioned publication; no acceptance or release completion is claimed here.
Final pre-format SHA256 fingerprints (source-only; root formatting will legitimately change them):
| Path below desktop src | SHA256 |
| --- | --- |
| `app.rs` | `4B700DBC6F8C0E8258B0F409C0B8F28266838F66ADAEABBEACE6B37E6DCAF736` |
| `behavior/scenario.rs` | `6E26E0EDD44D11D473B67E9DFE634185D77AE080E26776E9F55230D587ABA083` |
| `behavior/scenario/workspace.rs` | `D58CD2EA3F4603801BBD268906141076C5F70A83FB87A102B19A8806B083DEAE` |
| `behavior/scenario/workspace/tests.rs` | `CBA10D3A5C2EC0DF9E5FA67EA2B2570412DACE45BBD1D351431CC1579970C56D` |
| `behavior/scenario/focus.rs` | `48E1F227527E66CF0B56D75B23E05A1F4F89C3D0681760BE471D3E31362F2FBA` |
@@ -0,0 +1,56 @@
# Scenario parent focus — local execution gate
Codex-956atf `/root`, Windows, 2026-09-11. The corrected root source passes
**all five `scenario_form_focus` regressions**, zero failures or ignored tests.
This is the working patch over8d55d2d, not a claim that the unchanged base commit
contains these corrections. Four tests mount the actual App root at1100×700
through the headless WGPU renderer; missing backend support fails explicitly.
The [implementation handoff](release-scenario-focus-fix-20260911.md) records I1
parent rearming after source Back/Use/Escape and I2 delayed-operation ownership.
The [independent source review](release-scenario-focus-review-20260911.md) has no
unresolved material findings. Root preserved its strengthened overflow assertion:
initial parent offset must exceed64 and the focused Name must become visible.
## Actual integration and execution
The first desktop compile exited101 with12 installed-Iced API/test integration
errors. Root corrected owned dynamic widget IDs to `Id::from(String)`; added
the chooser's `Send + 'static` task bound; supplied the existing test Observer's
`reveal: None`; disambiguated Iced Element and core TransitionDirection imports;
and constructed the editor Cursor explicitly. The source-child and parent-focus
contracts were retained. No dependency or public model schema changed.
Scoped rustfmt and `git diff --check` passed. The second command was:
```text
cargo test --locked -p oa-desktop --bin arcadia-studio scenario_form_focus -- --test-threads=1
```
Exit0, warning-free compilation7m55s, test execution9.61s. Exactly5 passed;
715 other tests were filtered out. The current binary contains720 tests, but a
full720-suite result has not yet been established by this focused command.
Root selected the existing Rust/Cargo1.98.1 Windows GNU/LLVM toolchain and one
Cargo job. No test stack or renderer skip setting was added.
## Preserved evidence and limits
Immutable private directory `.local/scenario-focus-gate-20260911/` contains the
test executable, verified runtime DLL, failed and successful logs, and a275-input
manifest of actual source/build files, including the new untracked focus module.
The manifest explicitly identifies working-source provenance rather than claiming
all inputs were identical to base HEAD.
| Artifact | SHA256 |
| --- | --- |
| 135,520,256-byte test executable | `C9C4210EA36854F20C0DC9F38137CEFE5C3BC9CD19E10E1AD41DCB410CC92D04` |
| 190,464-byte libunwind.dll | `AA282FA47BB2C4E210CEA5E470CDBDCA58568F0BE0BC73E404D86AB9CD95EF6A` |
| First failed compile log | `D6552F4FB6A325E312241BF9035CD7F71877C2852D60D6FD7F425CB4394F1C27` |
| Passing focused log | `03DC0F104F1E4ADC7B26E25191ED9936A194EDBDF170182CAE02B4114B4E018D` |
| Input/artifact manifest | `C2D26460AF67D668F8159E1B2461C07110A0B2FD61938E410016FEADA7FB2D34` |
Full current desktop regression, current desktop Clippy, matching native GUI
focus timing/visual behavior and Linux desktop acceptance remain separate gates.
The isolated shared canvas candidate is not part of this binary; its future union
must preserve the exact parent marker, session/mount split, Name identity, sensor
and all five regressions. Prior676-suite/native80cf197 evidence remains historical.
@@ -0,0 +1,49 @@
# Scenario parent-focus correction — independent source review
Codex-956atf `/root/focus_review`, 2026-09-11. **No unresolved material source finding in the bounded I1/I2 correction. Runtime acceptance remains pending.** This reviews the working patch over HEAD `8d55d2d2eb05aed826b96a917cf776e1d8c3b690`, frozen with author `scenario_focus_fix`, including the final test-only strengthening below. The [pre-reboot integration review](release-chooser-contextual-integration-review-20260911.md) remains unchanged history. See the [implementation handoff](release-scenario-focus-fix-20260911.md).
Read AGENTS, current Gitea issue 1 coordination, empty open PR inventory, BehaviorAuthoring20260911 and ContextualSourceDrafts20260911 Wiki pages. Review ownership was posted at [issue 1/comment 302](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/1#issuecomment-302). Only this new report was written. No production edits, Cargo, rustc, rustfmt, tests, renderer creation, native UI, install or commit occurred. `git diff --check` passed for tracked working changes; it is not compilation or runtime evidence.
## Findings and resolution
**I1, previous P2 — correction supported by source.** `scenario.rs:317`, `:490`, `:669` and `:710` now request a fresh parent mount on successful Escape/Back/Use return. This changes only `form_mount` and `form_focus_pending`; `form_session` remains stable. Source staging and its existing admission/validation are retained. In `workspace.rs:362` the Name ID includes the current stamp; at `:642` the parent-only sensor is keyed by mount, while `editor_panel` remains keyed by editing session. The child still replaces the parent branch. An ordinary parent whose initial focus has already been consumed can therefore publish a fresh FormShown after the child returns. Raw semantic identities are not rotated to obtain focus.
**I2, previous P2 — correction supported by source.** `App::view` (`app.rs:1029`) wraps the final reference/hosted content with the scenario owner marker. `scenario_focus_owner` (`:1387`) computes the live document/scenario/revision/session/mount stamp and excludes busy, hosted, workflow, architecture, model-menu, file-prompt, deletion and pending-confirmation ownership. The marker is published from the current view on every operation traversal, rather than retained widget state. FormShown checks the context and consumes the exact mount before constructing the guarded task (`:1410`). Geometry epoch is deliberately absent from the stamp, so a same-mount viewport notification does not invalidate first focus.
`focus.rs:84–119` collects without changing any focusable or scrollable. A mismatched owner or missing exact Name target ends with `Outcome::None`. Only a valid target yields Apply, initially invalid; Apply rechecks the current root owner before changing focus or scroll (`:129–145`). Thus a saved S1 action operated after replacement, child opening, same-session remount, document/owner/revision change or modal ownership change cannot act on S2 or clear another field. The scoped Name ID also prevents the former global-ID collision.
**R1, P2 test weakness — corrected during review, execution pending.** The initial reveal test scrolled to 1000 and asserted only a final offset at most 64. Without proving actual overflow, that sequence could pass with a broken reveal. Author changed the production viewport to 1100×700, requires measured pre-focus offset greater than 64, and measures the Name's effective bounds inside the actual form viewport afterward (`workspace/tests.rs:217`). Existing raw/history assertions remain. No claim is made that this stronger fixture has passed; missing overflow will now fail explicitly. Author also added a direct chained-Apply owner-change falsifier (`:602`) after review feedback.
## Prepared regression scope
Five `scenario_form_focus_*` tests are present in `workspace/tests.rs`; four use the production headless renderer and actual `App::view`. Backend creation uses `expect`, with no absent-renderer/GPU skip. Tests remain under the existing debug-test module gate.
- The current-focus test retains stale-dispatch, repeated-dispatch, busy/hosted, epoch-only-change, raw snapshot, accepted-Project pointer and history assertions; it now executes the operation chain on the mounted production view and checks real reveal geometry.
- The return test (`:507`) first runs and consumes ordinary initial focus, mounts and focuses the actual source Name, then uses Back, valid Use and Escape independently. It checks stable session, changed mount, retained raw name/invocation/owner/slot/source-origin fields, focused parent, unchanged accepted Project/source bundle/history, and an unchanged returned recovery snapshot after focus. Use intentionally stages its source; the focus step must preserve that resulting snapshot.
- The saved-action matrix (`:553`) extracts S1's actual Widget action before eight live-state changes: replacement session, open source child, real Review candidate modal, document token, document revision, owner, same-session source-return mount, and hosted visibility. It operates on the rebuilt current `App::view`, preserving the focused child/Review or other actual input, source bundle, recovery state and Undo/Redo availability. This exercises delayed execution, not merely rejection of stale FormShown messages.
- The chained-Apply test (`:602`) successfully collects on the current parent, saves its Chain result, mounts/focuses a real Review candidate, and applies the saved operation there. It requires unchanged focus, recovery/source and history.
- Missing exact target (`:626`) deliberately uses a small generic marker/input tree with a valid stamp and an unrelated focused input. This is a narrow missing-target test, not claimed to mount the entire App.
`focus_action` extracts the first Widget action without waiting for task-stream completion while that action retains the channel. The execution helper follows `Outcome::Chain`. This matches the relevant synchronous chain behavior in the locally read Iced 0.14 runtime; no event-loop execution is claimed.
## Scroll, chooser and integration boundaries
Compared `behavior/scenario/focus.rs` with `forms/reference_chooser/trigger/focus.rs`. Both translate bounds by accumulated ancestor offsets, restore traversal state when leaving ancestors, calculate reveal from inner to outer scrollables, clamp shifts to content capacity, and apply shifts by traversal index. Local Iced `Scrollable::operate` reports its translation before traversing content, matching that calculation. Scenario Apply focuses the exact stamped Name; chooser focus has separate form scope/relative navigation/modal policy. Their root marker types are distinct. No concrete sign, ancestor-scope or retained-child conflict was found in this readback; native pixels and broader nested-scroll behavior remain execution work.
The production test helper calls `App::view`'s widget operation traversal. It does not invoke Iced UserInterface's additional overlay traversal. `reference_chooser::Popup::operate` (`widget.rs:252`) traverses its content, while its separate search input belongs to the overlay operation (`:484`). This report does **not** claim generic content traversal reaches that separate input. Existing child-open focus tasks and other focus systems were not redesigned or exhaustively audited.
Root owns scoped formatting, compilation, all five focus gates, related workspace/chooser/contextual-source tests, broader checks, matching GUI and native acceptance. Canvas integration must preserve the outer live owner marker, stamped Name, parent-only mount sensor and session/mount distinction. Root/author should link this review from their shared Wiki evidence; source review alone does not close the release or replace the frozen pre-reboot report.
## Reviewed byte fingerprints
SHA-256, actual pre-format freeze. Paths are relative to `crates/oa-desktop/src`. Formatting or further changes require fresh gate provenance.
| File | SHA-256 |
| --- | --- |
| `app.rs` | `4B700DBC6F8C0E8258B0F409C0B8F28266838F66ADAEABBEACE6B37E6DCAF736` |
| `behavior/scenario.rs` | `6E26E0EDD44D11D473B67E9DFE634185D77AE080E26776E9F55230D587ABA083` |
| `behavior/scenario/workspace.rs` | `D58CD2EA3F4603801BBD268906141076C5F70A83FB87A102B19A8806B083DEAE` |
| `behavior/scenario/focus.rs` | `48E1F227527E66CF0B56D75B23E05A1F4F89C3D0681760BE471D3E31362F2FBA` |
| `behavior/scenario/workspace/tests.rs` | `CBA10D3A5C2EC0DF9E5FA67EA2B2570412DACE45BBD1D351431CC1579970C56D` |
| `forms/reference_chooser/trigger/focus.rs` | `D489FA00F445BC603B3326CAD8721E2B608456ECF543954538904BF188C6C5FA` |
| `forms/reference_chooser/widget.rs` | `D66CC17AF7A48BB8598BE683F084BB08FA7AF7304F9636F0A1BEA94C5CF6C727` |