From 80f25882143d977ca4984691740c3a7ffd45aedd Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 11 Sep 2026 08:33:06 -0700 Subject: [PATCH] fix(forms): reset replacement sessions and preserve visible scroll --- .../src/app/reference_authoring/tests.rs | 33 +++- .../app/reference_authoring/tests/mounted.rs | 49 ++++-- crates/oa-desktop/src/behavior.rs | 1 + .../oa-desktop/src/behavior/form_session.rs | 148 ++++++++++++++++++ .../oa-desktop/src/behavior/scenario/focus.rs | 42 ++--- .../src/behavior/scenario/workspace.rs | 13 +- .../forms/reference_chooser/trigger/focus.rs | 14 +- ...elease-chooser-correction-gate-20260911.md | 58 +++++++ ...se-chooser-full-gate-diagnosis-20260911.md | 53 +++++++ .../release-scenario-focus-gate-20260911.md | 28 ++++ 10 files changed, 395 insertions(+), 44 deletions(-) create mode 100644 crates/oa-desktop/src/behavior/form_session.rs create mode 100644 harness/release-chooser-correction-gate-20260911.md create mode 100644 harness/release-chooser-full-gate-diagnosis-20260911.md diff --git a/crates/oa-desktop/src/app/reference_authoring/tests.rs b/crates/oa-desktop/src/app/reference_authoring/tests.rs index a71f5d08..12153549 100644 --- a/crates/oa-desktop/src/app/reference_authoring/tests.rs +++ b/crates/oa-desktop/src/app/reference_authoring/tests.rs @@ -425,6 +425,25 @@ fn review_reference_facets_groups_and_attachment_hosts_browse_without_replanning }; let mut app = App::with_project(p.clone()); let _ = app.update(Message::StartSemanticReview(Id::from_u128(100))); + if mode == 2 { + // A paired attachment is a real catalog facet only after explicit + // correspondence; selecting an unstaged synthetic pair is rejected. + for (id, oa) in [(103, true), (104, false)] { + let choice = crate::app::Choice { + id: Id::from_u128(id), + label: format!("Record · @object_{id}"), + }; + send_review( + &mut app, + if oa { + RowEvent::ReferenceOa(choice) + } else { + RowEvent::ReferenceSa(choice) + }, + ); + } + send_review(&mut app, RowEvent::AddReference); + } send_review(&mut app, RowEvent::Select(facet.clone())); send_review( &mut app, @@ -436,7 +455,19 @@ fn review_reference_facets_groups_and_attachment_hosts_browse_without_replanning } else { Id::from_u128(101) }; - assert_eq!(app.reference_draft(&host, path).unwrap().original, record); + assert_eq!( + app.reference_draft(&host, path) + .unwrap_or_else(|| { + panic!( + "Reference fixture mode={mode}, facet={facet:?}, selected={:?}, decisions={:?}, error={:?}", + review(&app).selected, + review(&app).decisions, + review(&app).error + ) + }) + .original, + record + ); let before = review(&app).recovery_snapshot().unwrap(); let plan = serde_json::to_value(&review(&app).plan).unwrap(); let proposed = review(&app).proposed.clone(); diff --git a/crates/oa-desktop/src/app/reference_authoring/tests/mounted.rs b/crates/oa-desktop/src/app/reference_authoring/tests/mounted.rs index b1e3bafb..e8a40656 100644 --- a/crates/oa-desktop/src/app/reference_authoring/tests/mounted.rs +++ b/crates/oa-desktop/src/app/reference_authoring/tests/mounted.rs @@ -760,22 +760,34 @@ async fn mounted_reverse_review_required_rationale_remains_editable_beside_candi let sa = p .elements .values() - .find(|element| { - element.layer() == Layer::Sa - && matches!( - element.shared(), - Some(SharedBody::Occurrence { - body: OccurrenceBody::Exchange { - operation: Some(_), - message_kind: MessageKind::Call | MessageKind::Asynchronous, - .. - }, - .. - }) - ) - }) + .find(|element| element.alias == "operation_probe_43") .unwrap() .id; + let allocation = p + .relations + .values() + .find(|relation| relation.alias == "operation_probe_30") + .unwrap() + .id; + // The boundary fixture deliberately has no selected operation. Author one + // through the validated command path before testing reverse omission. + let mut message = p.elements[&sa].clone(); + let Some(SharedBody::Occurrence { + body: OccurrenceBody::Exchange { operation, .. }, + .. + }) = message.shared_mut() + else { + panic!("The operation fixture must contain an Exchange call") + }; + *operation = Some(OperationUse { + allocation: Some(allocation), + }); + p = execute_batch(&p, &[Command::EditElement(sa, message)]).unwrap(); + assert!(matches!( + oa_core::operations::resolve(&p, sa).unwrap(), + oa_core::operations::OperationResolution::Selected(value) + if value.allocation == allocation + )); let inspector_id = Id::new_v4(); element( &mut p, @@ -836,7 +848,14 @@ async fn mounted_reverse_review_required_rationale_remains_editable_beside_candi let candidate = serde_json::to_value( review(&app) .reference_form(app.project()) - .unwrap() + .unwrap_or_else(|| { + panic!( + "Reverse review candidate: selected={:?}, decisions={:?}, error={:?}", + review(&app).selected, + review(&app).decisions, + review(&app).error + ) + }) .recovery_snapshot(), ) .unwrap(); diff --git a/crates/oa-desktop/src/behavior.rs b/crates/oa-desktop/src/behavior.rs index 11e08d1c..db74fc61 100644 --- a/crates/oa-desktop/src/behavior.rs +++ b/crates/oa-desktop/src/behavior.rs @@ -17,6 +17,7 @@ use std::collections::BTreeMap; #[cfg(test)] mod contextual_source_tests; pub(crate) mod creation; +mod form_session; pub(crate) mod scenario; // Projection acceptance precedes the coordinated scenario renderer integration. #[cfg(test)] diff --git a/crates/oa-desktop/src/behavior/form_session.rs b/crates/oa-desktop/src/behavior/form_session.rs new file mode 100644 index 00000000..cd324f0d --- /dev/null +++ b/crates/oa-desktop/src/behavior/form_session.rs @@ -0,0 +1,148 @@ +//! Keep behavior field state within one editing session and reset it on replacement. +use iced::advanced::widget::{Operation, Tree, tree}; +use iced::advanced::{Clipboard, Layout, Shell, Widget, layout, overlay, renderer}; +use iced::{Element, Event, Length, Rectangle, Size, Theme, Vector, mouse}; +use oa_core::Id; + +struct Identity(Id); + +struct Session<'a, Message, R: renderer::Renderer> { + identity: Id, + content: Element<'a, Message, Theme, R>, +} + +pub(super) fn reset_on_change<'a, Message: 'a, R: renderer::Renderer + 'a>( + identity: Id, + content: impl Into>, +) -> Element<'a, Message, Theme, R> { + Element::new(Session { + identity, + content: content.into(), + }) +} + +impl Widget for Session<'_, Message, R> { + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(Identity(self.identity)) + } + + fn children(&self) -> Vec { + vec![Tree::new(self.content.as_widget())] + } + + fn diff(&self, tree: &mut Tree) { + let retained = tree.state.downcast_mut::(); + if retained.0 != self.identity { + retained.0 = self.identity; + // Iced's keyed column preserves same-length child replacements. + // An editing-session replacement must instead get fresh field state. + tree.children = self.children(); + } else { + tree.diff_children(std::slice::from_ref(&self.content)); + } + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn layout(&mut self, tree: &mut Tree, renderer: &R, limits: &layout::Limits) -> layout::Node { + self.content + .as_widget_mut() + .layout(&mut tree.children[0], renderer, limits) + } + + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &R, + operation: &mut dyn Operation, + ) { + self.content + .as_widget_mut() + .operate(&mut tree.children[0], layout, renderer, operation); + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &R, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.content.as_widget_mut().update( + &mut tree.children[0], + event, + layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ); + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut R, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + style, + layout, + cursor, + viewport, + ); + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &R, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + &tree.children[0], + layout, + cursor, + viewport, + renderer, + ) + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'b>, + renderer: &R, + viewport: &Rectangle, + translation: Vector, + ) -> Option> { + self.content.as_widget_mut().overlay( + &mut tree.children[0], + layout, + renderer, + viewport, + translation, + ) + } +} diff --git a/crates/oa-desktop/src/behavior/scenario/focus.rs b/crates/oa-desktop/src/behavior/scenario/focus.rs index 189cbd24..9f6b0b73 100644 --- a/crates/oa-desktop/src/behavior/scenario/focus.rs +++ b/crates/oa-desktop/src/behavior/scenario/focus.rs @@ -232,25 +232,31 @@ impl Operation for Collect { }; let mut shifts = BTreeMap::new(); for ancestor in ancestors.iter().rev() { + let dx = reveal( + bounds.x, + bounds.width, + ancestor.bounds.x, + ancestor.bounds.width, + ); + let dy = reveal( + bounds.y, + bounds.height, + ancestor.bounds.y, + ancestor.bounds.height, + ); + // Drawing offsets are rounded; an already-visible axis must not + // acquire a scroll delta from a fractional capacity alone. let delta = Vector::new( - (ancestor.offset.x - + reveal( - bounds.x, - bounds.width, - ancestor.bounds.x, - ancestor.bounds.width, - )) - .clamp(0., ancestor.capacity.x) - - ancestor.offset.x, - (ancestor.offset.y - + reveal( - bounds.y, - bounds.height, - ancestor.bounds.y, - ancestor.bounds.height, - )) - .clamp(0., ancestor.capacity.y) - - ancestor.offset.y, + if dx == 0. { + 0. + } else { + (ancestor.offset.x + dx).clamp(0., ancestor.capacity.x) - ancestor.offset.x + }, + if dy == 0. { + 0. + } else { + (ancestor.offset.y + dy).clamp(0., ancestor.capacity.y) - ancestor.offset.y + }, ); if delta != Vector::ZERO { shifts.insert(ancestor.index, delta); diff --git a/crates/oa-desktop/src/behavior/scenario/workspace.rs b/crates/oa-desktop/src/behavior/scenario/workspace.rs index a1e18e4f..e643b949 100644 --- a/crates/oa-desktop/src/behavior/scenario/workspace.rs +++ b/crates/oa-desktop/src/behavior/scenario/workspace.rs @@ -1,7 +1,7 @@ //! Scenario presentation. Layout/camera controls never construct model commands. -use super::super::scenario_scroll; +use super::super::{form_session, scenario_scroll}; use super::*; -use iced::widget::{Space, column, keyed_column, sensor}; +use iced::widget::{Space, column, sensor}; pub(crate) const NAME_INPUT: &str = "scenario-form-name"; @@ -754,16 +754,13 @@ fn editor_panel<'a, R: iced::advanced::text::Renderer + 'a>( container( column![ container(actions).id("scenario-actions").width(Fill), - keyed_column([( + form_session::reset_on_change( session, scrollable(fields) .id("scenario-form") .width(Fill) - .height(Fill) - .into(), - )]) - .width(Fill) - .height(Fill), + .height(Fill), + ), ] .spacing(6), ) diff --git a/crates/oa-desktop/src/forms/reference_chooser/trigger/focus.rs b/crates/oa-desktop/src/forms/reference_chooser/trigger/focus.rs index 5705ca55..c3260108 100644 --- a/crates/oa-desktop/src/forms/reference_chooser/trigger/focus.rs +++ b/crates/oa-desktop/src/forms/reference_chooser/trigger/focus.rs @@ -183,9 +183,19 @@ impl Operation for Collect { ancestor.bounds.y, ancestor.bounds.height, ); + // Iced reports rounded drawing offsets, while capacity can be + // fractional. Clamping an already-visible axis can move it a pixel. let delta = Vector::new( - (ancestor.offset.x + dx).clamp(0., ancestor.capacity.x) - ancestor.offset.x, - (ancestor.offset.y + dy).clamp(0., ancestor.capacity.y) - ancestor.offset.y, + if dx == 0. { + 0. + } else { + (ancestor.offset.x + dx).clamp(0., ancestor.capacity.x) - ancestor.offset.x + }, + if dy == 0. { + 0. + } else { + (ancestor.offset.y + dy).clamp(0., ancestor.capacity.y) - ancestor.offset.y + }, ); if delta != Vector::ZERO { shifts.insert(ancestor.index, delta); diff --git a/harness/release-chooser-correction-gate-20260911.md b/harness/release-chooser-correction-gate-20260911.md new file mode 100644 index 00000000..fa18ac17 --- /dev/null +++ b/harness/release-chooser-correction-gate-20260911.md @@ -0,0 +1,58 @@ +# Chooser and editing-session corrections — Windows gate + +Codex-956atf `/root`, 2026-09-11. The corrected source passes **chooser14/14** and +**scenario-workspace18/18**, including all four previously failing cases and all +five parent-focus I1/I2 regressions. No failed or ignored tests. The full720 rerun +is active; its terminal result remains a separate gate. + +The [first full run](release-scenario-focus-gate-20260911.md#subsequent-full-suite-failure-and-corrective-work) +finished716 passed/4 failed. Its evidence is preserved. The +[chooser diagnosis](release-chooser-full-gate-diagnosis-20260911.md) records why +an already-visible anchor drifted by1px and why the rationale/attachment fixtures +stopped before their intended interaction. Exact assertions were retained. + +Root replaced the one-child keyed-column assumption with a small generic +`behavior/form_session.rs` widget. It forwards layout/input/drawing/operations, +retains child state for the same editing identity, and creates a fresh child tree +when a different session replaces it without an intervening closed render. The +existing test now verifies zero new-form scroll while preserving the canvas's +actual137×850 offset, same-session700px scroll, raw recovery and source/history. +The helper is shared with the future isolated process-editor union. + +The chooser and parent focus collectors now preserve zero requested reveal on +each axis. Rounded drawing offsets no longer create a spurious delta from a +fractional scroll capacity. The exact pointer-versus-keyboard anchor assertion +now passes. The corrected operation fixture explicitly selects and resolves its +actual interface operation; the paired-attachment fixture establishes real child +correspondence through public events before selecting its facet. Neither change +relaxes production ownership/write guards. + +## Actual commands and evidence + +Root used Rust/Cargo1.98.1 Windows GNU/LLVM, one Cargo job and ordinary test-stack +settings. Standalone scoped rustfmt and `git diff --check` pass. + +| Command | Result | +| --- | --- | +| `cargo test --locked -p oa-desktop --bin arcadia-studio app::reference_authoring::tests:: -- --test-threads=1` | Exit0; warning-free compile2m01s;14 passed,706 filtered;64.11s. | +| Same prefix with `behavior::scenario::workspace::tests::` | Exit0;18 passed,702 filtered;4.89s. | + +Immutable `.local/chooser-correction-gate-20260911/` contains the corrected +executable, verified runtime DLL and exact focused logs. Its512-input manifest +includes Rust/Cargo inputs plus shared Windows build resources, docs, examples +and harness fixtures. The earlier275-input manifest remains a narrower preserved +source inventory for the earlier executable, not this corrected gate. + +| Artifact | SHA256 | +| --- | --- | +| 135,405,568-byte corrected test executable | `05811E18E4FF3495EECFD585947234906F27F7709B7E583F0362477EF1A943C3` | +| 190,464-byte libunwind.dll | `AA282FA47BB2C4E210CEA5E470CDBDCA58568F0BE0BC73E404D86AB9CD95EF6A` | +| Chooser14 log | `EFA48AE5C66C8D839635F61DE2F9C798DF809FA686B3F173FE112B28558AAAF4` | +| Scenario18 log | `10B7EF829A7AF0D741571701EC257D88B71A0D5EF7BB53A2022AE000B5FF1CEE` | +| Working-input/artifact manifest | `985C06A3B9926C9096F97D59DDE57CE3494F2FE06008BB9F46FBD5578126B4BB` | + +Independent follow-up review of root's session wrapper and parent reveal +correction is active. Full720/current desktop Clippy and matching native Windows +GUI focus/visual acceptance remain open. The isolated shared canvas and schema8 +codec changes are outside this build. Further Linux builds/checks are deferred +by the user's15:28 UTC instruction until the user reports networking resolved. diff --git a/harness/release-chooser-full-gate-diagnosis-20260911.md b/harness/release-chooser-full-gate-diagnosis-20260911.md new file mode 100644 index 00000000..b4241eb2 --- /dev/null +++ b/harness/release-chooser-full-gate-diagnosis-20260911.md @@ -0,0 +1,53 @@ +# Chooser full-gate diagnosis — 2026-09-11 + +Codex-956atf `/root/chooser_gate_debug`. Initial read-only source diagnosis, followed by root's narrow three-file correction lease after the suite reached terminal failure. No compiler, saved-executable, native, WSL, network/VPN, or Git actions. Root remains sole execution/integration owner. Read AGENTS.md, current issue 1 comments through 346, empty open PR inventory, ScenarioFocus20260911 Wiki and prior chooser production review. This report supersedes no historical result. + +## Actual evidence + +Root identifies source `8895dd2e0f87c0f16085bf4b478b8c0fbed38685` and immutable `.local/scenario-focus-gate-20260911/arcadia_studio-e358a5ed59b1de41.exe`, SHA256 `C9C4210EA36854F20C0DC9F38137CEFE5C3BC9CD19E10E1AD41DCB410CC92D04`. Reviewer independently read the terminal `.local/logs/scenario-focus-full720-20260911.log`: **716 passed, 4 failed, 0 ignored**, 377.91 seconds. Root records log SHA256 `4A765FFAD4B08EC0B9D6B8399CF841E3C776051EA07B4433BB47C8E6BDC4C67F`. Reviewer did not execute it. Three assigned failures are below; scenario fresh-session scroll reset is root-owned and outside this diagnosis. + +Paths below are relative to `crates/oa-desktop/src` unless otherwise specified. + +## Rationale fixture stops before any widget interaction + +Actual failure: `app/reference_authoring/tests/mounted.rs:777`, Option unwrap while finding an SA Exchange occurrence whose operation is `Some`. No rationale field is mounted, clicked, or typed before this failure. + +The loaded `harness/fixtures/interface-operation-probe/output/boundary-only.json:188–228` has calls `operation_probe_43` and `operation_probe_44` with no authored operation field. The fixture represents an unselected operation. Existing `semantic_review/source7_tests.rs:285–301` shows the established explicit setup: select call 43, set `OperationUse { allocation: Some(operation_probe_30) }`, then validate via `execute_batch`. + +Minimal correction suggestion: prepare an explicitly operation-bearing call using that established setup before the search/reverse-review construction, and assert successful actual operation resolution. Do not merely broaden the search to accept `None`: that would no longer establish the operation-omission premise. Preserve every per-key addressed rationale event, final `Engineering reason`, retained focus, candidate recovery, Inspector raw, SourceBundle and Undo assertion. + +Production path to verify after setup: `reference_authoring.rs::reference_overlay` selects modal form ownership; `forms/reference_chooser/widget.rs:282–284` calls `trigger::own_input`; `trigger/focus.rs::exclusive` uses the whole `reference-owning-review-modal` boundary; `semantic_review/row.rs:182` receives `OperationRationale`. The current source boundary preserves sibling inputs; this failing execution neither proves nor disproves that behavior. + +## Keyboard return moves an already-visible anchor by one pixel + +Actual failure: `app/reference_authoring/tests/mounted.rs:463`, after pointer Open and Escape return, keyboard Open anchor is `Rectangle { x: 0.0, y: 124.79999, width: 300.0, height: 16.0 }`; original expected is identical except `y: 123.79999`. The preparatory actual-scroll assertion, pointer message assertion and first exact pointer anchor equality already passed. + +Concrete source defect: `forms/reference_chooser/trigger/focus.rs:185–188` computes `(ancestor.offset + reveal_delta).clamp(0, capacity) - ancestor.offset` even when reveal_delta is zero. The installed Iced 0.14.2 `scrollable.rs:549–559` passes the rounded drawing translation to the operation; `scrollable.rs:1876–1901` rounds each axis. Capacity derives from fractional content minus viewport size. A rounded END offset can exceed that fractional capacity; clamping then creates a negative delta for an already-visible field. Iced `State::scroll` adds that delta to the unrounded underlying absolute offset (`scrollable.rs:1790–1802`), potentially crossing the adjacent pixel boundary. This predicts precisely the observed +1px anchor. + +Minimal correction suggestion: zero requested reveal on an axis must produce exactly zero applied delta on that axis; only bound an axis that actually needs revealing. Preserve the exact rectangle equality; adding a tolerance would hide an unintended scroll. Keep existing fresh entry, nested reveal, return-after-scrolling-away and ownership tests. + +Needed falsifier: root narrow reproduction logging field bounds, ancestor rounded offset, fractional capacity, dx/dy and applied delta immediately before return. Expected defect signature is dx=dy=0 with a negative nonzero clamp delta at END. After correction, return should leave the scroll unchanged and both pointer/keyboard anchors equal. No runtime instrumentation has yet been applied. + +## Paired attachment facet may not be legitimately staged + +Actual failure: `app/reference_authoring/tests.rs:439`, first `reference_draft(host,path).unwrap()` in the loop over scalar, constructor group and child attachment. The log lacks the loop mode, active selected facet and errors. No chooser Open occurs in the failing iteration. + +Strong source hypothesis: mode 2 constructs `Child { oa: 103, sa: 104 }` and sends Select without staging this pair or establishing 103→104 correspondence. `oa-core/src/semantic/catalog.rs:690–703` derives child pairs from the reference map. Without that mapping, the current catalog has separate unpaired child selectors. Batch Select still invokes refresh (`semantic_review/batch.rs:395–400,584–586`); `semantic_review/row.rs:863–875` replaces selection if its selector is absent from current facts. Later Decide stores the explicit Candidate facet and attachment draft, but does not select it. `row.rs:529–545` correctly rejects a host facet that is not actively selected. + +The scalar banked-row reference test passed in the same executable, and `Document::from_project` explicitly proves model equality after source roundtrip, so source normalization loss is unsupported here. The paired-attachment diagnosis still requires actual mode/selection evidence. + +Needed falsifier: enrich the failing assertion with mode, requested facet, current selected facet, decisions, state error, candidate roots and candidate draft availability. If mode2/selection mismatch is confirmed, prepare its pair through the actual Attachment Pick/Stage workflow before Select (or establish explicit correspondence through its real workflow), retaining the old-facet rejection and source/history comparisons. Do not relax the production active-facet guard to accept a banked/unselected draft. If another mode fails, re-localize against its actual gate before changing source. + +## Handoff + +Root granted the narrow lease after reviewing the actual failure evidence. Three files were edited and standalone rustfmt completed exit 0 with edition2024 and skip_children=true: + +| File | Frozen correction SHA256 | +| --- | --- | +| `forms/reference_chooser/trigger/focus.rs` | `F1E79F4B03B2F9FD1CDC335054D4AE177DC9E759B6EA3D334B2BCCA39B57191B` | +| `app/reference_authoring/tests/mounted.rs` | `3A43E32BD028D4DF699F905BDBFAA7B99A56D5ADB5EB190AB6E47E14F6D7520C` | +| `app/reference_authoring/tests.rs` | `AAE2D95305DBCA6120DCFBFBF292F138C39015F136F3C85784CA46B0669729B8` | + +The production correction guards each zero reveal axis before the capacity clamp. The rationale fixture follows the existing validated EditElement operation-selection setup and additionally proves actual Selected resolution. Mode2 uses the real public ReferenceOa, ReferenceSa and AddReference events to establish103→104 correspondence before selecting its paired Child catalog facet; semantic-review visibility and write guards are unchanged. Candidate fixture failures now include mode/facet/selected/decisions/error diagnostics. Exact anchor, key counts, final rationale, stale-facet rejection, candidate staging, SourceBundle and history expectations were retained. + +These are prepared source corrections, not passing execution evidence. Root owns targeted chooser and full-suite reruns and integration with its separate session-scroll correction. Existing scenario parent-focus5/5 and independent canvas/schema8 work remain separate. diff --git a/harness/release-scenario-focus-gate-20260911.md b/harness/release-scenario-focus-gate-20260911.md index e9f467f4..8b63bfa9 100644 --- a/harness/release-scenario-focus-gate-20260911.md +++ b/harness/release-scenario-focus-gate-20260911.md @@ -41,6 +41,11 @@ 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. +The correction is now committed as +`8895dd2e0f87c0f16085bf4b478b8c0fbed38685`, pushed and exact readback verified. +Root independently compared all275 manifest inputs with that commit's Git archive; +there were zero mismatches. `commit-readback.json` preserves the comparison. + | Artifact | SHA256 | | --- | --- | | 135,520,256-byte test executable | `C9C4210EA36854F20C0DC9F38137CEFE5C3BC9CD19E10E1AD41DCB410CC92D04` | @@ -54,3 +59,26 @@ 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. + +## Subsequent full-suite failure and corrective work + +Root ran the preserved executable with `--test-threads=1` and captured the complete +720-test run: exit101,716 passed/4 failed, no ignored tests,377.91s. All five parent +focus tests pass within this run. The saved full log SHA256 is +`4A765FFAD4B08EC0B9D6B8399CF841E3C776051EA07B4433BB47C8E6BDC4C67F`. + +Three failures concern chooser fixtures/scrolling; the separately +[recorded diagnosis](release-chooser-full-gate-diagnosis-20260911.md) preserves +exact panic lines and values. The fourth is the existing +`new_scenario_form_resets_only_its_scroll_and_recovery_gets_a_fresh_session`: +after Cancel→Start without a closed render, the new form retained700px instead +of zero. Installed Iced0.14.2 keyed-column diff does not replace same-length child +state merely because its key changed. Root introduced a small explicit editing +session wrapper which replaces only its child tree on identity change and retains +same-session field state. The original complete canvas/source/history assertions +are unchanged. The wrapper is reusable for the isolated process editor's invocation. + +Root also applies the chooser's zero-reveal-axis correction to the parent focus +collector's identical fractional-capacity calculation. Corrected source is not +yet covered by this saved binary; a new focused/full gate is required. No fixture +tolerance, disabled test, stack override or renderer skip was introduced.