From cf4ad159f3323d5f7598371f5e23e175b66fcb77 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 19:25:20 +0000 Subject: [PATCH 01/25] feat(desktop): separate setup workflows and pin run controls (cherry picked from commit 8bf5f41feea2e06d2b94b9b2e83382091e6cf3a7) --- docs/plans/ui-refinement/execution-ledger.md | 48 +++ docs/plans/ui-refinement/issues.md | 13 + docs/plans/ui-refinement/plan.md | 172 +++++++++ .../reports/implementation-review.md | 80 ++++ .../reports/native-validation-handoff.md | 81 ++++ .../ui-refinement/reports/plan-review.md | 76 ++++ .../reports/standards-and-design.md | 36 ++ .../ui-refinement/reports/verification.md | 95 +++++ src/bin/desktop/configured_controls.rs | 33 +- src/bin/desktop/setup_ui_tests.rs | 348 ++++++++++++++++++ src/bin/rheon-desktop.rs | 214 +++++++++-- 11 files changed, 1154 insertions(+), 42 deletions(-) create mode 100644 docs/plans/ui-refinement/execution-ledger.md create mode 100644 docs/plans/ui-refinement/issues.md create mode 100644 docs/plans/ui-refinement/plan.md create mode 100644 docs/plans/ui-refinement/reports/implementation-review.md create mode 100644 docs/plans/ui-refinement/reports/native-validation-handoff.md create mode 100644 docs/plans/ui-refinement/reports/plan-review.md create mode 100644 docs/plans/ui-refinement/reports/standards-and-design.md create mode 100644 docs/plans/ui-refinement/reports/verification.md create mode 100644 src/bin/desktop/setup_ui_tests.rs diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md new file mode 100644 index 00000000..64a09c93 --- /dev/null +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -0,0 +1,48 @@ +# UI refinement execution ledger + +## 2026-10-20 — plan checkpoint + +- Revalidated remote main `fd7c0f0`; isolated local branch/worktree created from + that exact main. Original `/workspace/Rheon` remains clean at `6ba41c7`. +- Reused accepted Library audit version 0; no duplicate artifact created. +- No AGENTS.md, repository `.agents/skills` or executor-owned coding skill was + found. Standards authority current main is + `0fc92a24279f3a4574c80af0a2f31ea347a21597`. +- No standards application MCP exists in current catalog. Standards-authoring + skill explicitly excludes ordinary source edits and was inspected only for + applicability. Source-guided review does not claim engine certification. +- No DISPLAY/WAYLAND_DISPLAY/XDG_RUNTIME_DIR or X socket available. Native + evidence is blocked; no GUI, numerical solver, model or scientific work run. +- Pinned Rust 1.92.0/Cargo 1.92.0/Clippy 0.1.92 available. Affected baseline + desktop check started with locked dependencies; results pending. +- First slice is reversible UI composition and retains main-only dependency + strategy. Inspector validator's branch and test repair remain separate. + +## 2026-10-20 — M1 implementation and review checkpoint + +- Plan/design reviewed independently before source edits; PR1–PR3 conditions + incorporated. Operation start admitted M1; implementation continued serially. + Pinned source validate_plan returned valid after structural alignment. This + checks artifact structure, not design correctness or engine certification. +- Implemented explicit Preview/Accepted Session selection, one launch action, + shared carrier inputs for Session, persistent action/status strip, bounded + immutable status details, frozen running-request summary and collapsed + secondary comparison/reset actions. No numeric/default/worker/recording owner + changed; no Inspector/D1/README/agent writes. +- Independent source review found and resolved stale active-rerun form projection, + missing keyboard access to long feedback and omitted sealed-domain wording. +- Existing first-frame dock regression then failed with nested bottom-panel + placement. Superseded that mechanism with a top-down persistent strip before + the ScrollArea; reviewer accepted the simpler repair. Unchanged dock test passed. + No acceptance test was weakened and no height-cache/extra-pass machinery added. +- Final formatting and strict affected desktop/test Clippy passed. Five new + UI-only tests and three existing inert UI regressions passed. Focused evidence + now checks actual form scrolling, read-only Ctrl+End/select/copy and preserved + full feedback. No launch intent was dispatched by tests and no workload ran. +- [Verification](reports/verification.md) records commands/results and repaired + failures. [Source review](reports/implementation-review.md) binds final source + hashes. [Native handoff](reports/native-validation-handoff.md) names the next + environment-qualified procedure. No native capture/interaction, 40% text, + usability, scientific or whole-plan acceptance claim is made. +- M1 is Verifying pending native evidence. M2–M4 remain Planned. Branch/worktree + remain retained for parent review; no push, PR, merge, rebase or cleanup. diff --git a/docs/plans/ui-refinement/issues.md b/docs/plans/ui-refinement/issues.md new file mode 100644 index 00000000..f507f24f --- /dev/null +++ b/docs/plans/ui-refinement/issues.md @@ -0,0 +1,13 @@ +# UI refinement issues + +| ID | Finding / owner | Disposition / revisit | +| --- | --- | --- | +| UI-1 | No native display/session; UI task + existing native validator | Required native acceptance blocked. Execute exact handoff after local source/test checkpoint; do not substitute browser/headless claims. | +| UI-2 | No application standards-engine tool; UI lead | Current pinned standards read directly; no automated conformance certification. Revisit if authoritative application tool becomes available. | +| UI-3 | Separate local Inspector slice/dependency and test-only Clippy fix; Inspector validator | Excluded from M1. Reconcile final validated source before M4; parent relays coordination. | +| UI-4 | 40% visible-word/usability target needs current matched native baseline; native owner | No result claimed. Preserve warning/numerical context regardless of count; evaluate full staged refinement. | + +Resolved within M1: active-rerun UI identity mismatch, long-status keyboard access, +sealed-domain caption preservation and first-frame nested-panel overflow. See +[implementation review](reports/implementation-review.md) and +[verification](reports/verification.md); no unresolved material source finding. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md new file mode 100644 index 00000000..030f5829 --- /dev/null +++ b/docs/plans/ui-refinement/plan.md @@ -0,0 +1,172 @@ +# Focused desktop UI refinement + +**Plan status:** `Active` +**Current phase:** M1 native verification (source checkpoint complete) +**Next slice:** M1 — native visual/interaction validation in the existing authorized environment +**Acceptance status:** `blocked` + +**Execution ledger:** [execution-ledger.md](execution-ledger.md) + +**Issues:** [issues.md](issues.md) + +[Standards/design](reports/standards-and-design.md) · [Plan review](reports/plan-review.md) · +[Implementation review](reports/implementation-review.md) · [Verification](reports/verification.md) · +[Native handoff](reports/native-validation-handoff.md) + +## Objective + +Reduce visible text and competing controls while making Rheon's actual native +application easier to use. The user accepted audit +`libfile_825ba1b07a3c8191a340bb761eea1c89`, version `0`, +`rheon-ui-audit-2026-10-20.txt`, then authorized a standards-compliant plan and +implementation. Canonical admission is this path, operation `start` after the +independent plan review; subsequent implementation uses `continue`. No optional +approval is required for the reversible first slice. + +Base remote main revalidated 2026-10-20: +`fd7c0f0aee63bc8710df99f9bb01154e7619b974`. Local branch +`implementation/ui-hierarchy-20261020`, worktree `/workspace/Rheon-ui-hierarchy`. +UI task owns implementation and serial integration; parent owns eventual main +integration. Branch is private, retained for review/validation. No push, PR, +merge, rebase, branch deletion or cleanup is authorized in this turn. + +## Scope and non-goals + +First: explicit preview versus Accepted Session setup, one primary launch at a +time, fixed run-action/status area, unchanged launch validation and worker +ownership. Later: collapse optional setup prose, shared control geometry and +accessible help, camera/playback consistency, D1 comparison/archive presentation. +Keep units, relevant limitations, blocking errors, absence/uncertainty, pressure +stage/time and integrity-versus-completeness distinctions accessible and explicit. + +No numerical/tolerance/default-physics changes, solver/model/evaluator/corpus +execution, L1/MMS work, storage migration, new framework/dependency, web/Pages +redesign or wholesale D1 replacement. Do not modify README or local agent work. +No speculative integration of Inspector changes. + +## Dependency strategy and ownership + +M1 is based on main alone and does not depend on local Inspector slice +`241dfb5237db0fa064d1257f4baf12781e0a0d44` or dependency +`1424f2d55323825c8907cf9209914f29130ec7de511`. Their separate validator task +`01a15a9f-ffcc-75ee-bd76-02f1c277f027` owns validation and its test-only Clippy +repair. Preserve its branches; do not copy/rebase/merge their code. Parent relays +coordination because cross-thread messaging is unavailable in this environment. +M4 must revalidate that work's final source before admitting Inspector edits. + +## Milestones + +| Milestone | Goal and exact production write set | Dependency / gate | State | +| --- | --- | --- | --- | +| M1 | One selected setup workflow and persistent run controls. `src/bin/rheon-desktop.rs` (sidebar, presentation methods, UI state/tests only), `src/bin/desktop/configured_controls.rs` (form/action split and labels only), optional `src/bin/desktop/setup_ui_tests.rs` | Independent plan/design review; A1–A3 plus A6 supporting checks; A4 native evidence remains required | Verifying | +| M2 | Compact optional setup, aligned fields and keyboard-accessible help. `src/bin/desktop/{settings,collision_ui,emitter_controls}.rs`; optional new `src/bin/desktop/form_controls.rs` | M1 native feedback; preserve policy and unit editing; A4–A5 | Planned | +| M3 | Camera/transport consistency. `src/bin/desktop/{viewport,particle_view,engineering,transport_view}.rs`; optional `src/bin/desktop/playback_controls.rs` | M1/M2; separately re-admit exact main-file playback regions if needed; focus and session-ownership evidence | Planned | +| M4 | Promote existing D1 comparison/archive results. `src/bin/desktop/{comparison,comparison_field_view,archive_view,run_set_view,inspector}.rs` and `src/bin/desktop/ai_comparison/{mod,diagnostic_view}.rs` | M3 and final Inspector revision reconciled; re-admit exact composition-root regions; saved-fixture integrity/absence evidence | Planned | + +Production write sets are disjoint across planned milestones. A later need to +touch earlier integration regions is an explicit serial admission, never +parallel ownership. Only the UI lead writes this plan directory; reviewers own +non-authorizing review reports. Authored plan, ledger, issues and review reports +may change throughout. Generated logs/renders remain outside Git per +`docs/REPOSITORY_OUTPUTS.md`. + +## Binding Decisions + +1. `App` owns the setup selection; it does not change `material_mode` or any + numerical settings when switched. Preview retains material/carrier choice. + Accepted Session exposes its own physics form plus the existing shared + domain, grid, steps, maximum dt, pressure, preview, resource and CPU settings + that its existing launch consumes. Hide irrelevant emitter/mesh/material + controls for that workflow; do not hide consumed launch inputs. +2. Existing `start`, `start_configured`, validation, immutable intent publication, + `Worker`, `poll`, cancellation and child cleanup remain authoritative. UI + selection is disabled during active work. No new simulation loop or copied + worker state. Settings/Inspector switching must retain run control access. + Active work shows only frozen Request facts, not retained next-run form values. +3. Split configured form painting from its launch button so one fixed action + area above the scrolling form can return intent. Paint the current form and + process readiness invalidation before executing intent through existing launch + validation. Keep package inspection/readiness and + invalidation in `ConfiguredControls`; do not expose its private representation. +4. Preserve full status/error text in keyboard-reachable, height-capped scrolling + Status details; keep a concise single-line projection visible outside it so + long paths/errors cannot displace Cancel or the form. Shorten routine labels + and status copy only. + Existing string status is not parsed to invent severity/completion semantics. + Compact progress uses the existing worker's actual numerator/denominator; + cancellation remains the existing operation, not inferred success. +5. Keep advanced secondary actions available under task-named disclosures. + Preserve relevant limitations and clearing/retention consequences. No + unexplained icons or reduced body font/hit targets to meet text targets. +6. No browser renderer is substituted for actual native validation. The audit's + historical `a552d0a` capture is not a current baseline. + +## Objective Acceptance + +| ID | Observable criterion | Kind / environment / mode | Status | Evidence | +| --- | --- | --- | --- | --- | +| A1 | Actual egui pointer selection presents only the selected setup form and one primary launch; switching preserves settings and never launches work | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | +| A2 | Primary action/status remain outside scroll content, including Inspector mode; shared consumed inputs remain reachable; disabled package launch remains explicit | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | +| A3 | Launch/request/worker/cancel/recording semantics unchanged; no Inspector/D1/numerical edits; unit and limitation copy retained | focused / not-applicable / manual source review | satisfied | [Verification](reports/verification.md) | +| A4 | Native 1250×820 and 900×650 at 100% and 150% UI scale: no clipped labels/overlap; mode/action/status visible; scroll/keyboard/focus/help and in-progress cancellation work | user-workflow / required-real supported native display / manual | blocked | No display/session here | +| A5 | Matched current before/after states show materially less text with unchanged values/warnings; target 40% fewer visible explanatory words across full planned refinement, not M1 alone; 4/5 users identify workflow/edit material/find launch within 30s | user-workflow / representative native / manual | pending | No counts/usability results yet | +| A6 | Rust 1.92 formatting, affected desktop build, strict affected-target Clippy and focused UI tests pass | focused / Linux pinned Rust / automated | satisfied | [Verification](reports/verification.md) | + +Before expanding beyond M1, native owner observes real default/switched/Inspector +screens and overflow; that observation can revise panel placement/width, not +scientific semantics. Run ongoing-worker/cancel UI coverage only with an approved +UI-only fixture or independently authorized run; this turn authorizes no solver +or model execution. Headless paint establishes emitted layout/input behavior, +not human perception, native scaling, accessibility or complete workflows. + +## Simplicity And Ownership Review + +**Applicability:** `applicable` + +The desktop composition root changes. + +- Independent concepts and dimensions: App selects displayed setup and routes intent; ConfiguredControls + owns package/form readiness; worker owns execution; recording owns durable + truth. Only presentation timing/placement changes for desktop users. +- State, identity, value, time, policy, and mechanism: required interleaving is selected intent plus actual worker busy state. + Avoid coupling UI selection to physics mode, timeline selection or archives. +- Caller and composition-root knowledge: App knows the selected workflow and asks ConfiguredControls to paint its + form/action; it does not know package pin details or invent execution status. +- Representative change paths and forced owners: new workflow label changes only presentation. New configured readiness rule + stays in ConfiguredControls. Cancellation still changes only worker owner. +- Stable Interfaces versus hidden knowledge: the form/action interface carries enabled state and action intent; it does + not leak provider layout, source pins or shutdown ordering. +- Independent evolution, testing, failure, and replacement: forms are paintable/testable without execution. Existing worker failures + remain full status feedback; a failed package check cannot enable launch. +- Deletion and cumulative machinery result: no permanent registry, adapter or generic UI framework is introduced. Removing + selection/placement would restore competing launch forms, so that mechanism + contains the user-facing hierarchy requirement. Ordinary functions suffice. +- Necessary complexity and containment: inherent complexity is preserving two existing run profiles and shared + inputs; contain it in App's form composition. No new persisted UI schema, + parallel state model or solver abstraction is retained. + +## Rollback and re-plan + +M1 is presentation-only, reversible by reverting its isolated local diff/commit; +no migration, archive deletion or saved-data rewrite is needed. Preserve branch +and worktree until parent accepts a terminal disposition. Rollback must not +reset another worktree or alter the Inspector/numerical branches. + +## Re-Plan Triggers + +Re-plan for missing consumed launch inputs, changed package/worker ownership, +native evidence contradicting placement, overlap with Inspector integration, +unintended numerical changes or an inadequate UI-test oracle. Fix ordinary +lint/test failures inside the admitted boundary without restarting design. + +## Blockers + +Native display and representative user evidence are unavailable, so objective +acceptance remains incomplete. No standards application MCP is exposed; direct +source standards review is used and no engine conformance certificate claimed. +Build/tool availability and any baseline lint failures are recorded in the ledger. + +## Final Acceptance + +Native visual/interaction and matched-state usability claims remain unsatisfied. +No delivery, standards-engine certification or whole-plan acceptance is claimed. diff --git a/docs/plans/ui-refinement/reports/implementation-review.md b/docs/plans/ui-refinement/reports/implementation-review.md new file mode 100644 index 00000000..860b4fc6 --- /dev/null +++ b/docs/plans/ui-refinement/reports/implementation-review.md @@ -0,0 +1,80 @@ +# Independent first-slice source and evidence review + +Reviewed 2026-10-20 by independent `ui_source_review`. Base is +`fd7c0f0aee63bc8710df99f9bb01154e7619b974`; subject is M1's local production +diff and `setup_ui_tests.rs`, against the canonical plan and prior plan review. +This report is not native visual acceptance, numerical qualification or a +standards-engine conformance certificate. + +## Final disposition + +IR1–IR4 were repaired and independently re-read. M1's bounded source change is +suitable to retain for native validation; no remaining material source issue +was identified in this scope. Objective visual/interaction +acceptance remains blocked by the missing native display, and later milestones +remain unaccepted. This disposition authorizes no publication or integration. + +## Findings + +| ID | Severity | Initial evidence and consequence | Disposition | +| --- | --- | --- | --- | +| IR1 | Medium | `sidebar` initially derived the displayed Setup label from the active immutable Request, while `controls` selected the disabled form from retained `self.setup_workflow`. Saved reruns could therefore show the opposite form and stale values beneath the actual profile label. | Resolved: shared `Request::setup_workflow` feeds the selector and `running_setup_ui`; active work paints only a frozen-Request summary, not the next-run form. The pure summary test reads physical samples, domain, horizon and maximum dt from an inert Request; no Worker is constructed. Actual running/native coverage remains required. | +| IR2 | Medium | Full Status details initially contained a plain label inside a capped ScrollArea. Pinned egui 0.33.3 has no keyboard scrolling there, and ordinary selectable labels are not tab targets. Keyboard users lacked an in-app route to its clipped tail. | Resolved: immutable `&str`-backed multiline TextEdit preserves full feedback with normal focus/cursor/selection behavior inside the cap. The egui test proves focused Ctrl+End, Select All and exact Copy while attempted editing leaves authoritative status unchanged. Native Tab traversal/focus visibility remain required. | +| IR3 | Low | Shortened configured profile captions initially omitted the old explicit closed-domain word “sealed”; “no inlet” alone does not establish no outlet. | Resolved: both setup and frozen-request summary retain “Sealed full liquid · starts at rest · no inlet.” | +| IR4 | Medium | The unchanged existing `docked_inspector_cannot_overlap_viewport_at_supported_window_sizes` test initially failed on its first egui frame: the nested bottom panel started with a default height shorter than its content and expanded past the dock/window bottom before its cached height settled. Warmed-up new tests did not establish this first-frame property. | Resolved: a normal top-down action/status strip precedes the ScrollArea. Persistence is required; bottom placement is not. The unchanged existing first-frame dock test now passes at both supported sizes, as do all five new tests. No guessed height, empty reservation, extra sizing/input pass or new UI state is retained. | + +## Source observations + +- Action-strip painting returns intent; App dispatches only after current form edits. + Configured freeze still checks the current inspection/path/provenance and + support before saving or launching. No stale action-strip result becomes authority + for package validity. +- Accepted Session shared controls select carrier sampling, pressure method and + tolerance independently of retained preview `material_mode`. Switching setup + changes presentation state only; it does not assign numerical settings or + launch a worker. +- Existing start/start_configured, immutable intent, Request, worker, polling, + cancellation, recording and source pin bodies are unchanged. Unit-bearing + inputs and the configured profile's limitations remain present. +- Primary action, progress, Cancel and status are outside the scrolling setup + content and remain in the Inspector tab composition. Long status has bounded + geometry and retains the original string; no parsing invents severity or + completion. +- Production writes are limited to the two admitted UI composition/form files; + focused tests are separate. Inspector, D1, numerical sources, root README, + local agent work and the separately validated Inspector branch are untouched. + +## Evidence reviewed and limits + +The final `/tmp/rheon-ui-focused-tests.log` showed five `ui_setup_` tests pass: +pointer setup selection/value preservation, carrier controls for Accepted +Session, bounded status/top-strip composition and Inspector composition plus focused +full-text navigation/copy, disabled uninspected adapter action, and inert frozen +Request summary. These are actual egui input/paint assertions, not screenshots or +a native process. The test source never dispatches solver/model launch intent. +The affected strict Clippy log reports success; formatting log has no findings. +`git diff --check` passed during this review. After the IR4 repair, the unchanged +existing dock, section-collapse and archive-transition tests each passed in +`/tmp/rheon-ui-existing-{dock,sidebar,archive-transition}.log`. Their real egui +assertions cover first-frame dock bounds, section state and stale Inspector +suppression respectively. They do not launch numerical workers. The lead owns +the final static-gate rerun and consolidated verification record. + +The final test-only strengthening was independently read: wheel input targets +the actual setup-form title region, the title must scroll out of view, and the +Simulate rectangle must remain fixed. This distinguishes real form scrolling +from a no-op wheel event. Test naming now reflects the top strip. Production +source hashes are unchanged; no further test execution was needed for this read. + +The summary fixture proves its projection, not a complete active-worker/rerun +workflow. Directly requesting text focus in an egui test proves focused editing +semantics, not real Tab traversal or native focus visibility. Native perception, +scaling, ongoing-worker Cancel, usability and 40% fewer visible words remain +unproved. No GUI, solver, model, evaluator, corpus or scientific experiment was +run by this reviewer. Only this report was authored. + +Final reviewed source SHA256 after the IR4 placement repair: + +- `src/bin/rheon-desktop.rs`: `3004e9bdf1b53c5dd079bca14dae9aaedcd85047f3d833a2934045fc06466882` +- `src/bin/desktop/configured_controls.rs`: `f4581090a79d9d9eed6c5fc3e6e53d0cd7b6b6bf26aa924a18aa4a038feb8ae4` +- `src/bin/desktop/setup_ui_tests.rs`: `ba81bde816aea562b40d11a2290b3b7099ef8c3b184010ec74b61f1717314dfc` diff --git a/docs/plans/ui-refinement/reports/native-validation-handoff.md b/docs/plans/ui-refinement/reports/native-validation-handoff.md new file mode 100644 index 00000000..9cfe82af --- /dev/null +++ b/docs/plans/ui-refinement/reports/native-validation-handoff.md @@ -0,0 +1,81 @@ +# M1 native validation handoff + +Owner: existing authorized native validation environment, coordinated by the +parent. Do not create a duplicate Inspector/validation effort. This source is +main-based UI work; do not merge/rebase the Inspector slice to obtain a display. +Its separate validator task is `01a15a9f-ffcc-75ee-bd76-02f1c277f027`. + +## Candidate and build custody + +Use the local checkpoint on `implementation/ui-hierarchy-20261020` at +`/workspace/Rheon-ui-hierarchy`; resolve and record its full commit before transfer. +Its base is `fd7c0f0aee63bc8710df99f9bb01154e7619b974`. Confirm the working tree +matches the checkpoint and record source/tree, dependency lock and actual binary +identity. Do not relabel another binary or the separate Inspector binary as this +candidate. Production source/test hashes are in the independent review. + +For a source-bound build use the existing `tools/build_qualified_desktop.py` +recipe documented by `docs/api/qualified-desktop-build.md`: exact clean committed +source, a fresh output outside all checkouts, declared existing trusted Cargo and +Rustup homes/PATH, Rust 1.92.0 and locked desktop features. Select actual paths in +the validation environment; do not copy this environment's credentials/settings. +The wrapper builds but does not execute either binary. Its sidecar is bounded +build-material evidence, not numerical or hermetic qualification. A developer +build is acceptable for exploratory UI feedback if identified as such, not for +the stronger source-custody claim. + +Launch only the desktop UI with no workload-triggering flags. Do not pass +`--auto-run`, `--run`, model/deployment options or any numerical CLI command. +Do not select Obstacle lab (initialization solves a projection) or run/advance +Transport. If the selected environment cannot display the app normally, report +that exact block; do not change sandbox/graphics/session settings to bypass it. + +## Procedure requiring no simulation/model workload + +1. Record OS, display/session, graphics backend, UI scale, actual window size, + binary/source identity and state isolation. Compare this candidate against a + separately identified main-base UI at 1250×820 and 900×650, 100% and 150% scale. +2. Capture default Simulation preview, Accepted Session, expanded Simulation, + Domain, recording/resource and CPU sections. Verify one primary launch, + reachable shared inputs/units, preserved profile limitations and no clipping. + Switch back and confirm edited next-run settings persist without executing. +3. Scroll each long form and open Inspector with no run selected. Confirm the + action/status strip remains visible and the viewport is not overlapped. + Open/close Status details; if long feedback is needed, use a rejected open of + a known nonexistent archive or an approved UI-only fixture, never a real run. + Confirm long feedback remains bounded, readable and exactly copyable. +4. Use keyboard Tab/Shift+Tab to reach Setup, action and Status details. Open the + disclosure and acquire its read-only text control without direct test focus + assignment. Navigate to the tail, select/copy and attempt text input; verify + immutable content, visible focus and a usable exit path. Test pointer + equivalence and ensure hiding a form does not strand focus. +5. Select Compare with CPU adapter without inspecting a package. Confirm the + primary paired action is disabled and its reason visible. Do not inspect or + execute a model package for this UI pass. Do not click any enabled run action. +6. Retain matched images with annotations for hierarchy/spacing/affordances and + record observed failures. Count visible explanatory words only for identical + states/window/scale; separate values/units/warnings from helper prose. The + 40% target belongs to full staged refinement, not an automatic M1 pass gate. + +## Active-worker evidence still required + +Actual progress, frozen-rerun summary, disabled setup selection and Cancel use +the unchanged real worker owner. They have not been exercised natively here. +Validate them only with an existing approved UI-only fixture or separate explicit +authority for an actual run; this handoff grants no simulation/model authority. +If neither is available, keep that claim blocked. Do not fabricate a worker, +relabel recorded images or treat the inert summary test as a live workflow. + +No completed/failed/cancelled run, persisted-source integrity/completeness or +model accuracy claim follows from startup, paint or the above idle-state checks. +Those states retain their existing owners and are separate from source/visual +evidence. Report each covered state/modality and every uncovered required claim. + +## Return evidence + +Return candidate/build identity, annotated current native captures, exact +pointer/keyboard operations and outcomes, size/scale coverage, focus/overflow +findings, permitted state coverage and blockers. Keep generated images/logs out +of repository commits. Parent can accept M1 only after the named native evidence +is satisfied, or keep it Verifying and revise the layout. Later M2–M4 remain +planned; reconcile the final Inspector source before admitting M4 edits. diff --git a/docs/plans/ui-refinement/reports/plan-review.md b/docs/plans/ui-refinement/reports/plan-review.md new file mode 100644 index 00000000..25201d5a --- /dev/null +++ b/docs/plans/ui-refinement/reports/plan-review.md @@ -0,0 +1,76 @@ +# Independent plan and design review + +Reviewed 2026-10-20 by the independent `ui_plan_review` agent. Production base: +`fd7c0f0aee63bc8710df99f9bb01154e7619b974`. Subject: the initial UI refinement +plan and standards/design report, before production implementation. Initial +plan SHA256: `bf0f3f2807386f1a22162aaeead908a8ee77509b95ef9742b40fe3cf1d43ed02`. +Initial standards/design report SHA256: +`86dd954abfdaa0249de457e7786ee40c207cf9ad0dfd5887c9aaad726937026e`. + +## Disposition + +The direction, main-only dependency strategy and bounded first production slice +are suitable. Start M1 after incorporating the conditions below; no further +optional user approval or broader design research is needed. This is a source +and plan review, not production acceptance or standards-engine certification. +Native visual, focus and ongoing-worker interaction claims remain unsatisfied. + +The lead accepted PR1 and PR2 before implementation: collect launch/cancel +intent, dispatch after form painting/readiness invalidation, and preserve full +status behind keyboard-reachable bounded details with a concise summary. The +lead also committed to effective-workflow carrier controls without mutating +retained material mode and to structural plan alignment. These are accepted +design dispositions; their implementation/test outcomes require later review. + +## Findings and implementation conditions + +| ID | Severity | Finding | Required disposition / evidence | +| --- | --- | --- | --- | +| PR1 | Medium | Moving the action area ahead of scrolling form painting can make execution observe the previous form values when edits and activation occur in one input frame. The configured package's readiness/invalidation remains owned by its form. | Paint the footer to collect intent, process current form edits, then execute the selected launch with existing current readiness/validation. Do not start work from footer painting before the form is processed. Add an inert UI/input regression or adequate construction/source proof; never launch a solver/model to test this ordering. | +| PR2 | Medium | An unbounded full status/error label in a fixed bottom area can grow until setup, status detail or Cancel is clipped, particularly for long local paths and validation errors. Merely moving the existing label does not establish persistent usable controls. | Bound status presentation geometry while retaining a visible meaningful cause and explicit keyboard-reachable full text/details. Preserve the complete original status and do not parse text to invent severity/completion. Exercise long/multiline feedback at supported sizes with egui layout assertions; retain native verification as required evidence. | +| PR3 | Low | The initial draft expresses the required plan facts in prose/table equivalents but does not yet match the available source `validate_plan` structural projection (exact headings/fields, separate Status/Evidence columns and named design probes). | Lead has identified this and will align the canonical artifact and invoke the pinned source check. A structural pass supports only plan shape; it is not application qualification or evidence that the design is correct. | + +The selected-workflow implementation must also enforce a specific existing +source constraint: `start_configured` uses carrier counts and pressure controls +regardless of retained preview `material_mode`. Accepted Session controls must +therefore render carrier semantics even when preview material mode remains true. +Do not change `material_mode` merely to reuse the old form. Verify shared domain, +grid/count-or-density, steps, maximum dt, pressure method/tolerance, display, +recording and CPU inputs remain reachable, retain values and feed their current +owners. This elaborates the plan's existing binding decision rather than adding +a new numerical feature. + +## Source and standards observations + +- Inspected actual App sidebar, controls, `start_configured` and configured + launch-input construction, plus `ConfiguredControls::show`, package readiness + and path/provenance invalidation. The original simultaneous forms and launch + actions substantiate M1's hierarchy change. +- `App` is the correct owner for setup selection and routing. Splitting form + painting from launch intent retains configured package authority and the + existing worker/immutable-intent lifecycle. No registry, alternative worker + state, new schema or compatibility layer is needed. +- Exact M1 production write boundaries exclude Inspector, D1 comparison, + numerical kernels, root README and agent files. Main-only work avoids + speculatively stacking the separately validated Inspector slice. Reconcile + its final revision before M4; do not edit its test-only Clippy repair here. +- The plan names one next slice, separates objective acceptance from local + supporting gates, provides explicit rollback and keeps later write sets + disjoint with serial re-admission for composition-root changes. +- The 40% word target is appropriately measured over matched contemporary + states, not asserted from source counts or used to discard units/warnings. +- Standards source `0fc92a24279f3a4574c80af0a2f31ea347a21597` Planning, + Implementation, Verification/GUI, Accessibility and Architecture support + bounded reversible implementation while the native evidence remains blocked. + The authoring-only skill is not an ordinary application coding requirement; + no unavailable MCP is represented as passed. +- Focus requirements need real native evidence. Egui pointer/paint tests prove + only their exact input/layout claims and cannot establish keyboard/native + perception, scaling, usability or screen-reader conformance by themselves. + +## Evidence and limits + +This review ran no solver, model, evaluator, corpus or GUI. No current native +display exists in this environment. It does not approve future Inspector +integration, publication, merge, numerical acceptance or branch cleanup. Only +this review report was authored; production and canonical plan were not edited. diff --git a/docs/plans/ui-refinement/reports/standards-and-design.md b/docs/plans/ui-refinement/reports/standards-and-design.md new file mode 100644 index 00000000..f62285c0 --- /dev/null +++ b/docs/plans/ui-refinement/reports/standards-and-design.md @@ -0,0 +1,36 @@ +# Standards route and first-slice design + +Authoritative source inspected: MrScripty/Coding-Standards +`0fc92a24279f3a4574c80af0a2f31ea347a21597`, current main on 2026-10-20. +Rheon's recent fluid-accuracy plan uses the same pin; older plans use `188beda1`. +No application MCP or executable engine certification is claimed. The standards +authoring skill is not applicable to ordinary application edits. No standards +source/configuration is changed. + +Read Core then Router. Applicable workflows: Planning with accepted audit as +discovery input, Implementation, Verification/GUI, Development Proportionality, +Commit (isolation/history ownership), Documentation (plan and handoff). Rust +profile and Rust API profile apply to native egui code and the coordinated +internal form/action method split; no externally supported Rust API changes. Accessibility, Diagnostics, Architecture +and Code Design apply to interaction/hierarchy, status projection and composition. +Existing Contracts/Concurrency invariants are preserved at call sites; no public +API, IPC, persistence, native build adapter or dependency changes are admitted. +Frontend web profile does not apply to native egui. Concurrent Plan Integration +does not apply: one lead serially owns the plan; independent review reports do +not authorize or concurrently mutate its decisions. + +| Obligation | Concrete design/evidence | +| --- | --- | +| Planning admission/lifecycle | Canonical plan path, start/continue binding, one next slice, explicit native blocker, milestone write sets and rollback | +| Proportionality | Accepted audit settles direction. Implement bounded reversible presentation after one independent plan review; avoid speculative framework rewrite or further broad UX research | +| Ownership/composition | App selects form and routes existing launch; ConfiguredControls retains readiness; Worker retains all actual work/cancellation; no duplicated worker state | +| Contracts | Setup switching does not mutate physics, clear archives, publish intent or launch; all consumed shared inputs remain reachable; validation/immutability unchanged | +| Diagnostics | Full existing error/status remains available; no text parsing to infer success/severity; integrity and completion retain distinct owners | +| Accessibility | Named mode/action controls with native keyboard activation and preserved units; moving focus/hiding active inputs requires a deliberate transition; actual native focus remains evidence need | +| Verification | Real egui input for selection/form/action projection plus source/static checks; native perception/scaling/keyboard/cancel coverage separately blocked | +| Rust quality | Pinned 1.92, locked dependency resolution, affected formatting/strict Clippy and focused UI tests; no execution of numerical test suite | +| Repository integrity | Isolated main-only branch, no Inspector/README/agent changes, generated evidence outside Git, no remote mutations | + +The first native observation will decide only whether the chosen fixed action placement +and form height work at supported sizes. Missing display does not prevent the +independently useful workflow separation; it prevents accepting the visual claim. diff --git a/docs/plans/ui-refinement/reports/verification.md b/docs/plans/ui-refinement/reports/verification.md new file mode 100644 index 00000000..0dd0f50e --- /dev/null +++ b/docs/plans/ui-refinement/reports/verification.md @@ -0,0 +1,95 @@ +# M1 implementation checkpoint and verification + +Base: remote main `fd7c0f0aee63bc8710df99f9bb01154e7619b974`, revalidated +before planning and after implementation on 2026-10-20. Branch: +`implementation/ui-hierarchy-20261020`; worktree `/workspace/Rheon-ui-hierarchy`. +The accepted audit remains Library file `libfile_825ba1b07a3c8191a340bb761eea1c89` +version 0. [Independent plan review](plan-review.md) preceded production edits; +[independent implementation review](implementation-review.md) assessed the +actual source and evidence, including the repairs below. + +## Implemented behavior + +- A named Setup selector chooses Simulation preview or Accepted Session. Only + the selected form and its primary launch action appear. Switching retains + preview material mode and current numerical settings; it does not launch. +- Shared domain, physical resolution, steps, maximum dt, pressure, display, + recording/resource and CPU inputs remain available to Accepted Session using + carrier semantics even when retained preview mode is material. +- Action, actual progress, Cancel and status live in a persistent top strip + above the scrolling form, including the Inspector tab. Buttons return intent; + dispatch follows current form edits and existing package/launch validation. +- Full status is retained in a height-capped read-only text control with + navigation/selection/copy, behind Status details. A concise projection stays + visible. No string parsing invents error, success or completeness states. +- During an actual worker, the setup area shows frozen Request facts rather + than unrelated next-run form values. The selected preference is not overwritten. +- Secondary comparison/reset actions are under Comparisons & reset; saved-file + and mesh retention is visible when that group is open. Sealed-domain and + model-scope limitations remain explicit for Accepted Session. + +No numerical implementation/default, request construction, start/validation, +worker/cancellation, immutable-intent publication, recording codec, D1 or +Inspector implementation was changed. Added Request::setup_workflow is a UI +projection of its existing configured-set presence. No new dependency/framework. + +## Executed checks + +Toolchain: rustc 1.92.0 (`ded5c06cf`), Cargo 1.92.0 (`344c4567c`), Clippy 0.1.92. +All Cargo commands use the existing isolated Rheon toolchain/cache configuration +from `/workspace/rheon-setup/env.sh`, `--locked`, desktop features and one build job. + +| Check | Actual outcome / proof boundary | +| --- | --- | +| `cargo check --locked --features desktop --bin rheon-desktop` | Passed for initial baseline; final source is also compiled by strict Clippy and test gates | +| `cargo fmt --all --check` | Passed after final source/test edits | +| `cargo clippy --locked --features desktop --bin rheon-desktop --tests -- -D warnings` | Passed; compiles/lints tests but does not execute numerical tests | +| `cargo test --locked --features desktop --bin rheon-desktop ui_setup_ -- --nocapture` | 5 passed, 0 failed; 152 filtered out | +| Same target, filter `sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input` | Existing test passed unchanged | +| Same target, filter `docked_inspector_cannot_overlap_viewport_at_supported_window_sizes` | Existing first-frame bounds test passed unchanged after placement repair | +| Same target, filter `archive_opening_frame_suppresses_the_previous_live_inspector` | Existing transition test passed unchanged; absent archive path only | +| Pinned source `standards_verifier.checks.plan_contract.validate_plan` | Canonical plan structurally valid; source helper with explicit Python paths, not application-engine qualification | +| `git diff --check` | Passed | + +Generated command logs are outside Git at `/tmp/rheon-ui-*.log`. Focused tests +exercise real egui pointer/paint events, not screenshots: mode selection, +preserved settings/no worker launch, carrier controls, disabled uninspected +adapter action, independent form scrolling while the action remains fixed, +Inspector composition, bounded long-status details, focused Ctrl+End navigation, +select-all/exact copy and attempted edit rejection, plus an inert Request summary. +No new test constructs a Worker or dispatches a launch. Existing selected tests +do not advance simulation. No model, corpus, evaluator or scientific suite ran. + +## Failures found and repaired + +1. Initial new tests used Rust struct-update syntax on Drop-owning App; compilation + rejected that move. Tests now use ordinary App construction/field assignment. +2. Review found a saved-rerun projection could mix an active profile with retained + next-run form settings. Active work now paints only its frozen Request summary. +3. Review found a plain Label inside ScrollArea did not provide keyboard access + to the tail of long feedback. Immutable multiline TextEdit plus focused + navigation/copy/immutability assertions replaced it. +4. Existing first-frame dock test exposed nested bottom-panel height-cache + overflow. Bottom placement was superseded by a normal top-down strip before + scrolling content, eliminating height estimation, extra sizing passes and + blank reserved space. The unchanged test then passed. No test weakened. + +These were ordinary M1 repairs. The placement change is recorded in the ledger; +it preserves the required persistent action behavior, authority and write scope. + +## Acceptance limits + +This is an implemented and source-reviewed M1 checkpoint, not whole-plan/native +acceptance. No native display/session exists here: DISPLAY, WAYLAND_DISPLAY and +XDG_RUNTIME_DIR are absent, and `/tmp/.X11-unix` does not exist. No native GUI was +started or captured. Headless input/paint tests do not prove native Tab order, +focus visibility, contrast, scaling, actual active-worker cancellation or usability. +Direct test focus assignment proves only behavior after focus is acquired. + +No 40% visible-text reduction, user timing, scientific validation or performance +gain is claimed. Contemporary matched native captures and user observations +remain required. The historical audit capture is not a contemporary baseline. +No standards application tool was exposed, and the source structural helper is +not a standards conformance certificate. Inspector slice/dependency remain +untouched and unvalidated by this task. [Native handoff](native-validation-handoff.md) +defines the next evidence boundary without authorizing numerical work. diff --git a/src/bin/desktop/configured_controls.rs b/src/bin/desktop/configured_controls.rs index d4a6ffe2..e5a296c3 100644 --- a/src/bin/desktop/configured_controls.rs +++ b/src/bin/desktop/configured_controls.rs @@ -344,14 +344,11 @@ impl ConfiguredControls { Ok(set) } - /// Return launch intent only. The caller freezes and saves the immutable - /// run-set before runtime preflight or execution in the existing worker. - pub(crate) fn show(&mut self, ui: &mut egui::Ui, enabled: bool) -> bool { - let mut launch = false; + pub(crate) fn show(&mut self, ui: &mut egui::Ui, enabled: bool) { ui.add_enabled_ui(enabled, |ui| { - ui.label("Accepted Session · sealed full liquid, constructor rest, zero inlet"); + ui.label("Sealed full liquid · starts at rest · no inlet"); ui.small("Constant forcing only; no appearance, emitter, phase source, or model accuracy claim."); - ui.checkbox(&mut self.paired, "Pair native baseline with configured CPU pressure"); + ui.checkbox(&mut self.paired, "Compare with CPU adapter"); ui.horizontal(|ui| { ui.label("Density (kg/m³)"); ui.add(egui::DragValue::new(&mut self.density).speed(1.0).range(1e-9..=1e9)); @@ -392,13 +389,31 @@ impl ConfiguredControls { if self.paired { self.package_controls(ui); } - let ready = !self.paired || self.current_inspection().is_ok(); - let label = if self.paired { "Freeze and run native + configured" } else { "Freeze and run native" }; - launch = ui.add_enabled(ready, egui::Button::new(label)).clicked(); if !self.feedback.is_empty() { ui.label(&self.feedback); } }); + } + + /// Returns intent only. The caller processes current form edits before + /// freezing and saving immutable intent through the existing launch path. + pub(crate) fn launch_button(&self, ui: &mut egui::Ui, enabled: bool) -> bool { + let ready = !self.paired || self.current_inspection().is_ok(); + let label = if self.paired { + "Save & run pair" + } else { + "Save & run native" + }; + let launch = ui + .add_enabled( + enabled && ready, + egui::Button::new(label).min_size(egui::vec2(ui.available_width(), 32.0)), + ) + .clicked(); + ui.small("Saves immutable intent before running."); + if !ready { + ui.small("Inspect the adapter package before running."); + } launch } diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs new file mode 100644 index 00000000..f761624d --- /dev/null +++ b/src/bin/desktop/setup_ui_tests.rs @@ -0,0 +1,348 @@ +//! UI-only input/layout checks. These tests never dispatch launch intent. +use super::*; + +fn frame( + ctx: &egui::Context, + size: egui::Vec2, + events: Vec, + mut paint: impl FnMut(&egui::Context), +) -> egui::FullOutput { + ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, size)), + events, + ..Default::default() + }, + |ctx| paint(ctx), + ) +} +fn labels(output: &egui::FullOutput) -> Vec<(String, egui::Rect)> { + output + .shapes + .iter() + .filter_map(|shape| match &shape.shape { + egui::Shape::Text(text) => { + let rect = egui::Rect::from_min_size(text.pos, text.galley.size()); + shape + .clip_rect + .intersects(rect) + .then(|| (text.galley.text().to_owned(), rect)) + } + _ => None, + }) + .collect() +} +fn has(output: &egui::FullOutput, label: &str) -> bool { + labels(output).iter().any(|(text, _)| text == label) +} +fn rect(output: &egui::FullOutput, label: &str) -> egui::Rect { + labels(output) + .into_iter() + .find(|(text, _)| text == label) + .unwrap_or_else(|| panic!("missing {label}: {:?}", labels(output))) + .1 +} +fn click( + ctx: &egui::Context, + size: egui::Vec2, + output: &egui::FullOutput, + label: &str, + mut paint: impl FnMut(&egui::Context), +) { + let pos = rect(output, label).center(); + for pressed in [true, false] { + let _ = frame( + ctx, + size, + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + &mut paint, + ); + } +} +fn context() -> egui::Context { + let ctx = egui::Context::default(); + ctx.style_mut(|style| style.animation_time = 0.0); + ctx +} + +#[test] +fn ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs() { + let ctx = context(); + let size = egui::vec2(1250.0, 820.0); + let mut app = App::default(); + app.steps = 37; + app.requested_dt = 0.013; + let before = ( + app.domain_side, + app.size, + app.materials.config.material, + app.selected, + ); + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + } + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(has(&output, "Simulate")); + assert!(!has(&output, "Save & run native")); + assert!(!has(&output, "Compare with CPU adapter")); + click(&ctx, size, &output, "Simulation preview", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + click(&ctx, size, &output, "Accepted Session", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(app.setup_workflow == SetupWorkflow::AcceptedSession); + assert!(has(&output, "Save & run native")); + assert!(!has(&output, "Simulate")); + assert!(has(&output, "Compare with CPU adapter")); + assert!(!has(&output, "Fluid material")); + assert!(!has(&output, "Emitter")); + assert_eq!( + before, + ( + app.domain_side, + app.size, + app.materials.config.material, + app.selected + ) + ); + assert!(app.material_mode); + assert_eq!(app.steps, 37); + assert_eq!(app.requested_dt, 0.013); + assert!(app.worker.is_none() && app.running_request.is_none() && app.history.is_none()); +} + +#[test] +fn ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode() { + let ctx = context(); + let size = egui::vec2(900.0, 2500.0); + let mut app = App::default(); + app.setup_workflow = SetupWorkflow::AcceptedSession; + let mut paint = |ctx: &egui::Context| { + egui::CentralPanel::default().show(ctx, |ui| app.controls(ui)); + }; + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], &mut paint); + } + let output = frame(&ctx, size, vec![], &mut paint); + click(&ctx, size, &output, "Simulation", &mut paint); + let output = frame(&ctx, size, vec![], &mut paint); + assert!(has(&output, "Numerical cells / axis (count mode)")); + assert!(has(&output, "Tight pressure tolerance")); + assert!(has(&output, "Maximum step")); + assert!(has(&output, "Steps")); + assert!(!has(&output, "Particle samples / side")); + assert!(!has(&output, "Liquid preview stiffness")); + assert!(!has(&output, "Liquid / gas material simulation")); + click(&ctx, size, &output, "Domain & environment", &mut paint); + let output = frame(&ctx, size, vec![], &mut paint); + assert!(has(&output, "Domain side")); + assert!(!has(&output, "Ambient / wall temp")); + assert!(app.material_mode); + assert!(app.worker.is_none()); +} + +#[test] +fn ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector() { + for size in [egui::vec2(900.0, 650.0), egui::vec2(1250.0, 820.0)] { + let ctx = context(); + let status = format!( + "Recording unavailable: {}\nPrevious data retained; select a writable directory.", + "long-path/".repeat(300) + ); + let mut app = App::default(); + app.status = status.clone(); + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + } + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + click(&ctx, size, &output, "Status details", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + let launch = rect(&output, "Simulate"); + assert!(launch.bottom() <= size.y && launch.top() > 0.0); + assert!(rect(&output, "Status details").bottom() < size.y); + assert!(output.shapes.iter().any(|shape| matches!(&shape.shape, + egui::Shape::Text(text) if text.galley.text() == status && shape.clip_rect.height() <= 100.0))); + let form_point = rect(&output, "Rheon").center() + egui::vec2(0.0, 30.0); + let _ = frame( + &ctx, + size, + vec![ + egui::Event::PointerMoved(form_point), + egui::Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: egui::vec2(0.0, -2000.0), + modifiers: Default::default(), + }, + ], + |ctx| { + app.sidebar(ctx); + }, + ); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert_eq!(launch, rect(&output, "Simulate")); + assert!( + !has(&output, "Rheon"), + "setup form should scroll independently" + ); + click(&ctx, size, &output, "Inspector", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(has(&output, "Numerical inspector")); + assert_eq!(launch, rect(&output, "Simulate")); + let text_id = egui::Id::new("run-status-text"); + ctx.memory_mut(|memory| memory.request_focus(text_id)); + let modifiers = egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }; + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key: egui::Key::End, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }], + |ctx| { + app.sidebar(ctx); + }, + ); + let state = egui::text_edit::TextEditState::load(&ctx, text_id).unwrap(); + assert_eq!( + state.cursor.char_range().unwrap().primary.index, + status.chars().count() + ); + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key: egui::Key::A, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }], + |ctx| { + app.sidebar(ctx); + }, + ); + let output = frame( + &ctx, + size, + vec![egui::Event::Copy, egui::Event::Text("unwanted edit".into())], + |ctx| { + app.sidebar(ctx); + }, + ); + assert!( + output + .platform_output + .commands + .iter() + .any(|command| matches!(command, + egui::OutputCommand::CopyText(text) if text == &status)) + ); + assert_eq!(app.status, status); + assert!(app.worker.is_none()); + } +} + +#[test] +fn ui_setup_uninspected_adapter_does_not_emit_launch_intent() { + let ctx = context(); + let size = egui::vec2(900.0, 1800.0); + let mut controls = configured_controls::ConfiguredControls::default(); + let mut requested = false; + let mut paint = |ctx: &egui::Context| { + egui::CentralPanel::default().show(ctx, |ui| { + controls.show(ui, true); + requested |= controls.launch_button(ui, true); + }); + }; + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], &mut paint); + } + let output = frame(&ctx, size, vec![], &mut paint); + click(&ctx, size, &output, "Compare with CPU adapter", &mut paint); + let output = frame(&ctx, size, vec![], &mut paint); + assert!(has(&output, "Inspect the adapter package before running.")); + click(&ctx, size, &output, "Save & run pair", &mut paint); + assert!(!requested); +} + +#[test] +fn ui_setup_running_summary_uses_frozen_physical_samples_and_timestep() { + // An inert request snapshot exercises projection without constructing a Worker. + let material = rheon::ParticleFluidConfig { + samples_per_axis: 7, + ..Default::default() + }; + let request = Request { + collision_mesh: None, + size: 32, + steps: 37, + tight: false, + threads: 1, + methods: vec![PressureImplementation::default()], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Adaptive, + domain_side: 2.0, + requested_dt: 0.013, + material: Some(material), + material_color: [0; 3], + recording_limits: Default::default(), + recording_directory: String::new(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + }; + let ctx = context(); + let output = frame(&ctx, egui::vec2(900.0, 650.0), vec![], |ctx| { + egui::CentralPanel::default().show(ctx, |ui| running_setup_ui(ui, &request)); + }); + assert!(has(&output, "Inputs frozen at launch.")); + assert!(has(&output, "7 samples/axis · domain 2.000 m")); + assert!(has(&output, "37 steps · maximum step 13.000 ms")); + assert!(has(&output, "Liquid / gas material preview")); + assert!(!has(&output, "Simulate")); +} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index d747e112..ec9585d5 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -54,6 +54,9 @@ use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; #[path = "desktop/settings.rs"] mod settings; +#[cfg(test)] +#[path = "desktop/setup_ui_tests.rs"] +mod setup_ui_tests; #[path = "desktop/transport_view.rs"] mod transport_view; #[path = "desktop/viewport.rs"] @@ -107,6 +110,13 @@ struct ConfiguredRequest { runtime: Option<(Arc, accepted_session::model_process::ChildSlot)>, } impl Request { + fn setup_workflow(&self) -> SetupWorkflow { + if self.configured_set.is_some() { + SetupWorkflow::AcceptedSession + } else { + SetupWorkflow::Preview + } + } fn preview_edge(&self) -> Result { self.preview_resolution .edge(self.domain_side, self.size as usize) @@ -1007,7 +1017,54 @@ fn run_material( } result } +#[derive(Clone, Copy, Default, PartialEq, Eq)] +enum SetupWorkflow { + #[default] + Preview, + AcceptedSession, +} +impl SetupWorkflow { + fn label(self) -> &'static str { + match self { + Self::Preview => "Simulation preview", + Self::AcceptedSession => "Accepted Session", + } + } +} +#[derive(Clone, Copy)] +enum SetupAction { + Run(SetupWorkflow), + Cancel, +} +fn running_setup_ui(ui: &mut egui::Ui, request: &Request) { + ui.heading("Running setup"); + ui.label(request.setup_workflow().label()); + ui.small("Inputs frozen at launch."); + let samples = request + .material + .map_or(request.size as usize, |material| material.samples_per_axis); + ui.label(format!( + "{samples} samples/axis · domain {:.3} m", + request.domain_side + )); + ui.label(format!( + "{} steps · maximum step {}", + request.steps, + settings::duration(request.requested_dt) + )); + if request.configured_set.is_some() { + ui.small("Sealed full liquid · starts at rest · no inlet"); + ui.small( + "Constant forcing only; no appearance, emitter, phase source, or model accuracy claim.", + ); + } else if request.material.is_some() { + ui.small("Liquid / gas material preview"); + } else { + ui.small("Filled carrier-fluid preview"); + } +} struct App { + setup_workflow: SetupWorkflow, ai_comparison: Option, archive: archive_view::ArchiveView, run_sets: run_set_view::RunSetView, @@ -1059,6 +1116,7 @@ impl Default for App { fn default() -> Self { let thread_limit = available_threads(); Self { + setup_workflow: SetupWorkflow::default(), ai_comparison: None, archive: archive_view::ArchiveView::default(), run_sets: run_set_view::RunSetView::default(), @@ -1145,8 +1203,31 @@ impl App { return; } ui.separator(); + let displayed_workflow = self + .running_request + .as_ref() + .filter(|_| self.worker.is_some()) + .map_or(self.setup_workflow, Request::setup_workflow); + ui.add_enabled_ui(self.worker.is_none() && !self.closing, |ui| { + egui::ComboBox::from_label("Setup") + .selected_text(displayed_workflow.label()) + .show_ui(ui, |ui| { + for workflow in [SetupWorkflow::Preview, SetupWorkflow::AcceptedSession] + { + ui.selectable_value( + &mut self.setup_workflow, + workflow, + workflow.label(), + ); + } + }); + }); + // Keep actions outside scrolling content. Dispatch only after this + // frame's form edits and package-readiness invalidation. + let action = self.run_controls(ui); + ui.separator(); egui::ScrollArea::vertical() - .id_salt("sidebar-content") + .id_salt(("sidebar-content", self.setup_workflow as u8)) .show(ui, |ui| { if self.inspector_open { ui.heading("Numerical inspector"); @@ -1167,19 +1248,88 @@ impl App { } else { ui.label("Start a simulation or open a numerical archive"); } + } else if let Some(request) = self + .running_request + .as_ref() + .filter(|_| self.worker.is_some()) + { + running_setup_ui(ui, request); } else { - if self.configured_controls.show(ui, self.worker.is_none()) - && let Err(error) = self.start_configured() - { - self.status = error; - } self.controls(ui); } }); + if let Some(action) = action { + match action { + SetupAction::Run(SetupWorkflow::Preview) => self.start(false), + SetupAction::Run(SetupWorkflow::AcceptedSession) => { + if let Err(error) = self.start_configured() { + self.status = error; + } + } + SetupAction::Cancel => self.cancel(), + } + ctx.request_repaint(); + } }) .response .rect } + fn run_controls(&mut self, ui: &mut egui::Ui) -> Option { + let mut action = None; + if let Some(worker) = &self.worker { + let n = worker.progress.load(Ordering::Relaxed); + ui.add( + egui::ProgressBar::new(n as f32 / worker.total as f32) + .text(format!("{n}/{} steps", worker.total)), + ); + if ui + .add_sized( + [ui.available_width(), 32.0], + egui::Button::new("Cancel simulation"), + ) + .clicked() + { + action = Some(SetupAction::Cancel); + } + if let Some((_, step, elapsed)) = worker.history.timing() { + ui.small(format!("Calculating step {step} · {elapsed:.3} s")); + } + } else { + let launch = match self.setup_workflow { + SetupWorkflow::Preview => ui + .add_enabled( + !self.closing, + egui::Button::new("Simulate") + .min_size(egui::vec2(ui.available_width(), 32.0)), + ) + .clicked(), + SetupWorkflow::AcceptedSession => { + self.configured_controls.launch_button(ui, !self.closing) + } + }; + if launch { + action = Some(SetupAction::Run(self.setup_workflow)); + } + } + // Keep long feedback from displacing run controls. The complete original + // message is available through a named keyboard-operable disclosure. + ui.add(egui::Label::new(&self.status).truncate()); + ui.collapsing("Status details", |ui| { + egui::ScrollArea::vertical() + .id_salt("run-status-details") + .max_height(96.0) + .show(ui, |ui| { + let mut status = self.status.as_str(); + ui.add( + egui::TextEdit::multiline(&mut status) + .id(egui::Id::new("run-status-text")) + .font(egui::TextStyle::Body) + .desired_width(f32::INFINITY), + ); + }); + }); + action + } fn reset_timeline(&mut self) { self.history = None; self.running_request = None; @@ -1670,6 +1820,8 @@ impl App { } } fn controls(&mut self, ui: &mut egui::Ui) { + let configured = self.setup_workflow == SetupWorkflow::AcceptedSession; + let material_preview = self.material_mode && !configured; ui.heading("Rheon"); ui.label( egui::RichText::new("LIVE FLUID LABORATORY") @@ -1680,9 +1832,9 @@ impl App { ui.add_enabled_ui(self.worker.is_none() && !self.closing, |ui| { egui::CollapsingHeader::new("Domain & environment").default_open(false).show(ui, |ui| { let previous_side=self.domain_side; - if self.material_mode {self.materials.domain(ui, &mut self.domain_side);} else { + if material_preview {self.materials.domain(ui, &mut self.domain_side);} else { settings::quantity(ui,"Domain side",&mut self.domain_side,"m",0.01,0.001..=100.0); - ui.small("Original demo: filled carrier fluid throughout the box."); + ui.small("Filled carrier fluid throughout the box."); } if self.domain_side!=previous_side { let scale=self.domain_side/previous_side; @@ -1690,6 +1842,9 @@ impl App { self.emitter.half_length*=scale;self.emitter.radius*=scale;self.emitter.nozzle_depth*=scale; } }); + if configured { + self.configured_controls.show(ui, self.worker.is_none()); + } else { if self.collision.show(ui, self.domain_side, self.material_mode) { self.reset_timeline(); } egui::CollapsingHeader::new("Fluid material").default_open(true).show(ui, |ui| { ui.add_enabled_ui(self.material_mode,|ui|self.materials.fluid(ui)); @@ -1701,17 +1856,19 @@ impl App { } else if self.material_mode {ui.small("Emission off; only the initial domain contents evolve.");} else {ui.small("Original smoke source; turns off halfway.");} }); + } egui::CollapsingHeader::new("Simulation").default_open(false).show(ui, |ui| { - if ui.checkbox(&mut self.material_mode,"Liquid / gas material simulation").changed() { + if !configured && ui.checkbox(&mut self.material_mode,"Liquid / gas material simulation").changed() { self.emitter.pressure_heads=if self.material_mode {[1000.0,800.0]} else {[0.12,0.08]}; } - ui.horizontal(|ui| {ui.label(if self.material_mode {"Adaptive preview edge"} else {"Numerical cells / axis (count mode)"});ui.add_enabled(self.material_mode || self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.size).range(4..=if self.material_mode {512}else{rheon::MAX_GRID_EDGE}).suffix("³"));}); + let material_preview = self.material_mode && !configured; + ui.horizontal(|ui| {ui.label(if material_preview {"Adaptive preview edge"} else {"Numerical cells / axis (count mode)"});ui.add_enabled(material_preview || self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.size).range(4..=if material_preview {512}else{rheon::MAX_GRID_EDGE}).suffix("³"));}); let mut physical = self.numerical_cells_per_metre.is_some(); if ui.checkbox(&mut physical,"Select physical sampling density").changed() { self.numerical_cells_per_metre = physical.then_some(32); } if let Some(density) = &mut self.numerical_cells_per_metre { - ui.add(egui::DragValue::new(density).range(1..=rheon::MAX_CELLS_PER_METRE).suffix(if self.material_mode {" particle samples/m"} else {" cells/m"})); + ui.add(egui::DragValue::new(density).range(1..=rheon::MAX_CELLS_PER_METRE).suffix(if material_preview {" particle samples/m"} else {" cells/m"})); ui.small("Counts = ceil(domain side × requested density); exact physical extent retained."); } egui::ComboBox::from_id_salt("preview-resolution-mode").selected_text(match self.preview_resolution { @@ -1742,7 +1899,11 @@ impl App { } ui.small("Carrier inventory: native face velocity, cell appearance tracer, accepted interval pressure. Material inventory: particle attributes and preintegration diagnostics. Other Rheon session/lab backends are not recorded here."); ui.horizontal(|ui| {ui.label("Timeline budget");ui.add(egui::DragValue::new(&mut self.history_budget_mib).range(16..=4096).suffix(" MiB"));}); - let sampling = self.selected_sampling(); + let sampling = if configured { + self.selected_carrier_count().map(|size| (size, None)) + } else { + self.selected_sampling() + }; match &sampling { Ok((size, material)) => { let numerical = material.map_or(*size as usize, |m|m.samples_per_axis); @@ -1763,10 +1924,10 @@ impl App { } Err(e) => {ui.colored_label(egui::Color32::LIGHT_RED,e);} } - ui.small("Particle samples control physical detail; preview voxels control the image."); + ui.small(if material_preview {"Particle samples control physical detail; preview voxels control the image."} else {"Numerical cells control physical detail; preview voxels control the image."}); settings::quantity(ui,"Maximum step",&mut self.requested_dt,"s",0.001,0.000001..=0.1); ui.small("Accepted time steps adapt to physical stability limits."); - if self.material_mode { + if material_preview { ui.horizontal(|ui| {ui.label("Particle samples / side");ui.add_enabled(self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.materials.config.samples_per_axis).range(4..=64));}); ui.horizontal(|ui| {ui.label("Particle limit");ui.add(egui::DragValue::new(&mut self.materials.config.max_particles).range(32768..=262144));}); @@ -1783,9 +1944,10 @@ impl App { ui.horizontal(|ui| {ui.add_enabled(!self.automatic_threads,egui::DragValue::new(&mut self.threads).range(1..=self.thread_limit));ui.label(format!("/ {} available",self.thread_limit));}); ui.checkbox(&mut self.show_workers,"Orange worker voxel boxes").on_hover_text("Sampled actual worker positions; diagnostics add calculation overhead."); }); - ui.add_space(8.0); - if ui.add_sized([ui.available_width(),32.0],egui::Button::new("▶ Simulate")).clicked() {self.start(false);} - if ui.button("Reset timeline (keep mesh)").clicked() { self.reset_timeline(); self.status = "Ready to repeat simulation with accepted mesh".into(); } + if !configured { + ui.collapsing("Comparisons & reset", |ui| { + if ui.button("Clear timeline").clicked() { self.reset_timeline(); self.status = "Ready to repeat simulation with accepted mesh".into(); } + ui.small("Keeps mesh and saved recordings."); if !self.material_mode { ui.checkbox(&mut self.save_native_set, "Save immutable native run set"); ui.small("Fixed actual dt; a shortened step retains its archive and reports unsupported saved intent."); @@ -1796,23 +1958,9 @@ impl App { comparison.set_steps(self.steps.clamp(1,512)); self.ai_comparison = Some(comparison); } - - }); - if let Some(w) = &self.worker { - ui.add_space(8.0); - let n = w.progress.load(Ordering::Relaxed); - ui.add( - egui::ProgressBar::new(n as f32 / w.total as f32) - .text(format!("{n}/{} steps", w.total)), - ); - if ui.button("■ Cancel simulation").clicked() { - self.cancel(); - } - if let Some((_, step, elapsed)) = w.history.timing() { - ui.label(format!("Calculating step {step} · {elapsed:.3} s")); + }); } - } - ui.label(&self.status); + }); egui::CollapsingHeader::new("Viewport & navigation").default_open(false).show(ui, |ui| { ui.checkbox(&mut self.viewport.touchpad_mode,"Touchpad scroll / two-finger orbit").on_hover_text("Enable when your touchpad reports wheel events. Shift pans; Ctrl zooms. Native pinch is supported."); ui.checkbox(&mut self.viewport.camera.orthographic,"Orthographic"); From 82086850e95ea0dc50ca40d63dd67d42c27e2f65 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 20:30:15 +0000 Subject: [PATCH 02/25] fix(desktop): wrap narrow setup forms and viewport captions (cherry picked from commit cb06a14b53a8506e82f9b1e9d06b9df22c80ce36) --- docs/plans/ui-refinement/execution-ledger.md | 25 +++++ docs/plans/ui-refinement/issues.md | 6 ++ docs/plans/ui-refinement/plan.md | 25 ++++- .../reports/width-repair-review.md | 52 +++++++++++ src/bin/desktop/configured_controls.rs | 10 +- src/bin/desktop/settings.rs | 8 +- src/bin/desktop/setup_ui_tests.rs | 92 +++++++++++++++++++ src/bin/desktop/viewport.rs | 48 ++++++---- src/bin/rheon-desktop.rs | 21 +++-- 9 files changed, 250 insertions(+), 37 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/width-repair-review.md diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 64a09c93..6c48631c 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -46,3 +46,28 @@ usability, scientific or whole-plan acceptance claim is made. - M1 is Verifying pending native evidence. M2–M4 remain Planned. Branch/worktree remain retained for parent review; no push, PR, merge, rebase or cleanup. + +## 2026-10-20 — M1 width repair after native feedback + +- Parent reports exact prerequisite `8bf5f41` passed controlled native build/hash + checks and exercised workflow switching, retention, scrolling, disabled adapter + launch, status copy/edit refusal, sizes/scales, archive refusal and close/reopen; + 30 actual quality-85 JPEGs retained by validator (delivery handled separately). +- Native owner observed expanded form text and narrow/150% viewport information + clipping. Reproduced form overflow in an inert egui regression before repair. + Converted form rows to wrapping layouts; wrapped the long recording disclosure + with its existing identity; wrapped appearance information/navigation captions. +- Independent review caught potential caption/mesh-note overlap. Derived mesh + note placement from actual caption height, wrapped that note, and checked an + inert single-triangle mesh in the regression. No mesh import or workload ran. +- Seven focused `ui_setup_` tests plus existing dock and sidebar-state regressions + pass. Locked affected desktop check, strict desktop/test Clippy, formatting and + diff whitespace checks pass on pinned Rust 1.92. Logs retained outside Git as + `/tmp/rheon-ui-width-{tests,dock,sidebar,check,clippy,fmt}.log`. +- This environment still has no DISPLAY, WAYLAND_DISPLAY or XDG_RUNTIME_DIR. + These are source/egui checks only. Exact repair needs native validation and new + actual JPEGs; prior captures depict only the prerequisite. Active progress, + Cancel, frozen rerun and native long-status stress remain unverified. +- Only the serial width-repair write set is admitted. No launch/request/worker, + recording, Inspector, scientific or dependency behavior changed. Preserve all + branches/artifacts; no publication, merge or cleanup performed. diff --git a/docs/plans/ui-refinement/issues.md b/docs/plans/ui-refinement/issues.md index f507f24f..6a7b7804 100644 --- a/docs/plans/ui-refinement/issues.md +++ b/docs/plans/ui-refinement/issues.md @@ -11,3 +11,9 @@ Resolved within M1: active-rerun UI identity mismatch, long-status keyboard acce sealed-domain caption preservation and first-frame nested-panel overflow. See [implementation review](reports/implementation-review.md) and [verification](reports/verification.md); no unresolved material source finding. + +M1 native feedback: parent reports prerequisite native inspection complete for +idle workflows; expanded form and viewport caption horizontal clipping found. +Width repair implemented with focused regressions; exact repaired revision awaits +native validation. Active progress/Cancel/frozen rerun and long-status stress are +still open evidence gaps. See the width-repair source review and latest ledger. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 030f5829..21082b5a 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Focused desktop UI refinement **Plan status:** `Active` -**Current phase:** M1 native verification (source checkpoint complete) -**Next slice:** M1 — native visual/interaction validation in the existing authorized environment +**Current phase:** M1 native clipping repair (exact repair awaits native verification) +**Next slice:** M1 — validate the width repair in the existing native environment **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -170,3 +170,24 @@ Build/tool availability and any baseline lint failures are recorded in the ledge Native visual/interaction and matched-state usability claims remain unsatisfied. No delivery, standards-engine certification or whole-plan acceptance is claimed. + +## M1 native-feedback repair admission — 2026-10-20 + +Parent reports controlled native validation of checkpoint `8bf5f41` found expanded +form text clipping and viewport information clipping at 900×650 / 150%. Only +width wrapping is admitted: the existing M1 form files/tests plus the exact +`settings.rs` form rows and `viewport.rs` appearance captions. This is a serial +repair admission, not the start of M2 or M3; their remaining work stays Planned. +Preserve strings, units, warnings, fonts, values, interaction order and worker +ownership. Keep recording disclosure identity when supplying a wrapped galley. + +Accept the repair only after focused UI regressions and compile/lint checks, +independent source review, then exact-revision native inspection of expanded +Preview/Accepted Session forms and appearance captions at 1250×820 and 900×650, +100% / 150%. Recheck independent scroll and input retention; capture actual +updated JPEGs at quality 85 with revision/state labels. No workload is needed. +Active progress/Cancel/frozen rerun and long-status stress remain unverified; +exercise only under the previously bounded fixture/run authority. Parent reports +30 retained native JPEGs of the prerequisite; no images or native verification +of this repair are claimed here. Roll back only the isolated repair commit if +needed; preserve the accepted prerequisite and unrelated Inspector work. diff --git a/docs/plans/ui-refinement/reports/width-repair-review.md b/docs/plans/ui-refinement/reports/width-repair-review.md new file mode 100644 index 00000000..150de4c4 --- /dev/null +++ b/docs/plans/ui-refinement/reports/width-repair-review.md @@ -0,0 +1,52 @@ +# Independent native-feedback width repair review + +Reviewed against prerequisite `8bf5f41feea2e06d2b94b9b2e83382091e6cf3a7` on +2026-10-20 by `ui_source_review`. This reviews the local width-repair diff, not +the prerequisite or later planned redesign. No native repair verification is +claimed; only this report was authored by the reviewer. + +## Disposition and finding + +No remaining material source finding in this bounded repair. It is suitable for +the declared static checks and exact-revision native verification; source review +does not close that native gate. + +**W1 — medium, resolved:** the initial wrapped information caption could overlap +the fixed-y collision annotation at narrow widths. The repair computes the +information galley first, then width-wraps and places the mesh note below its +actual height with an 8px gap. All policy/sampling text remains. The revised +300px viewport test uses an inert single-triangle mesh and asserts both text +widths and non-overlapping vertical bounds. No Worker or simulation is involved. + +## Other source checks + +- Wrapped form rows retain text, field order, numeric owners, ranges, units, + conversion, enabled state and returned numeric-widget responses. No scientific + or worker/recording contract changed. +- The recording disclosure's explicit original string salt matches the old + `ui.collapsing`/`CollapsingHeader::new` identity in pinned egui 0.33.3. Supplying + a wrapped galley preserves the full semantic label and normal disclosure + keyboard behavior; no new persisted identity is introduced. +- Information/navigation painter text preserves original copy, font sizes and + colors. Width-bounded layout fixes horizontal clipping; collision annotation + stacking now follows the laid-out information caption rather than a fixed y. +- `git diff --check` passed. The lead's focused width log showed seven UI tests + pass, including the loaded-mesh regression after W1 repair. Affected strict + Clippy reported success at the inspected intermediate diff; the lead owns the + final static-gate record. These prove their exact egui layout/input assertions + only. The expanded form fixture covers both setup workflows and opens the + recording/workspace disclosures where present. It is not a native 150% capture + or exhaustive package/mesh/error-state validation. + +No solver, model, evaluator, corpus, GUI or scientific workload was run by this +reviewer. Exact-revision native expanded-form, caption and interaction evidence +remains required after the repair. Do not infer a native pass from headless +layout tests or earlier JPEGs. + +Final reviewed source SHA256: + +- `src/bin/rheon-desktop.rs`: `f3b9bba3e6834a183220b4ff2f2b9413bb84d0f70dc951e4ee089458feb5e16d` +- `src/bin/desktop/settings.rs`: `ee0a8ccf666a917dec92ef695b4e55e32f268fd1681773324d821735bc4a0613` +- `src/bin/desktop/configured_controls.rs`: `5463e18b3b5a139c448dd327b23318605664e90aea4690187981177e7f68e60b` +- `src/bin/desktop/viewport.rs`: `e187ce147a09c2bd2ff789c0c2172e60e466538483a00dd4ec3ebbbc6642f3f2` +- `src/bin/desktop/setup_ui_tests.rs`: `f226120c32c7a20a71d5f7f9f282d17d1d58772354781a41b73514a6ea96b7f8` diff --git a/src/bin/desktop/configured_controls.rs b/src/bin/desktop/configured_controls.rs index e5a296c3..3cd0f1bf 100644 --- a/src/bin/desktop/configured_controls.rs +++ b/src/bin/desktop/configured_controls.rs @@ -349,13 +349,13 @@ impl ConfiguredControls { ui.label("Sealed full liquid · starts at rest · no inlet"); ui.small("Constant forcing only; no appearance, emitter, phase source, or model accuracy claim."); ui.checkbox(&mut self.paired, "Compare with CPU adapter"); - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label("Density (kg/m³)"); ui.add(egui::DragValue::new(&mut self.density).speed(1.0).range(1e-9..=1e9)); }); ui.checkbox(&mut self.newtonian, "Apply constant Newtonian viscosity"); if self.newtonian { - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label("Dynamic viscosity (Pa·s)"); ui.add(egui::DragValue::new(&mut self.dynamic_viscosity).speed(0.001).range(0.0..=1e5)); }); @@ -455,7 +455,7 @@ impl ConfiguredControls { if let Some(inspection) = &self.inspection { ui.label(&inspection.description); } - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label("Requested native prefix (intervals)"); ui.add( egui::DragValue::new(&mut self.requested_native_prefix_steps) @@ -466,7 +466,7 @@ impl ConfiguredControls { } fn vector_control(ui: &mut egui::Ui, label: &str, values: &mut [f64; 3]) { - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label(label); for value in values { ui.add(egui::DragValue::new(value).speed(0.01)); @@ -474,7 +474,7 @@ fn vector_control(ui: &mut egui::Ui, label: &str, values: &mut [f64; 3]) { }); } fn byte_control(ui: &mut egui::Ui, label: &str, bytes: &mut usize) { - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label(label); ui.add( egui::DragValue::new(bytes) diff --git a/src/bin/desktop/settings.rs b/src/bin/desktop/settings.rs index 6465c584..f651123f 100644 --- a/src/bin/desktop/settings.rs +++ b/src/bin/desktop/settings.rs @@ -74,12 +74,12 @@ impl MaterialControls { }); ui.small("Approximate reference presets; edit values below."); let m = &mut self.config.material; - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label("State"); ui.selectable_value(&mut m.phase, FluidPhase::Liquid, "Liquid"); ui.selectable_value(&mut m.phase, FluidPhase::Gas, "Gas"); }); - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label("Density").on_hover_text( "Mass per volume. Weight also depends on gravity. 1 g/mL = 1 mg/mm³ = 1,000 kg/m³.", ); @@ -236,7 +236,7 @@ impl MaterialControls { -100.0..=100.0, ); } - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { if ui.button("Earth").clicked() { self.config.gravity = [0.0, -9.80665, 0.0]; } @@ -255,7 +255,7 @@ pub fn quantity( speed: f64, range: std::ops::RangeInclusive, ) -> egui::Response { - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { ui.label(label); ui.add( egui::DragValue::new(value) diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index f761624d..da809b2b 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -346,3 +346,95 @@ fn ui_setup_running_summary_uses_frozen_physical_samples_and_timestep() { assert!(has(&output, "Liquid / gas material preview")); assert!(!has(&output, "Simulate")); } + +#[test] +fn ui_setup_expanded_forms_keep_text_inside_sidebar_width() { + for workflow in [SetupWorkflow::Preview, SetupWorkflow::AcceptedSession] { + let ctx = context(); + let size = egui::vec2(600.0, 6000.0); + let mut app = App::default(); + app.setup_workflow = workflow; + let mut paint = |ctx: &egui::Context| { + egui::SidePanel::left("narrow-form") + .exact_width(280.0) + .resizable(false) + .show(ctx, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| app.controls(ui)); + }); + }; + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], &mut paint); + } + for section in [ + "Domain & environment", + "Simulation", + "Numerical recording (every accepted step)", + "Workspace limits", + "Viewport & navigation", + "CPU workers", + ] { + let output = frame(&ctx, size, vec![], &mut paint); + if has(&output, section) { + click(&ctx, size, &output, section, &mut paint); + } + } + let output = frame(&ctx, size, vec![], &mut paint); + let overflow: Vec<_> = output + .shapes + .iter() + .filter_map(|shape| { + if let egui::Shape::Text(text) = &shape.shape { + let right = text.pos.x + text.galley.size().x; + (right > shape.clip_rect.right() + 1.0).then(|| text.galley.text().to_owned()) + } else { + None + } + }) + .collect(); + assert!( + overflow.is_empty(), + "horizontally clipped form text: {overflow:?}" + ); + assert!(app.worker.is_none()); + } +} + +#[test] +fn ui_setup_viewport_caption_wraps_at_narrow_scaled_width() { + let ctx = context(); + let output = frame(&ctx, egui::vec2(300.0, 300.0), vec![], |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let mut viewport = viewport::Viewport::default(); + viewport.collision_mesh = Some(Arc::new( + rheon::CollisionMesh::new( + vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], + vec![[0, 1, 2]], + rheon::MeshPolicy::TwoSidedSurface, + ) + .unwrap(), + )); + viewport.show(ui, None, None, &[], 32, None); + }); + }); + let (_, bounds) = labels(&output) + .into_iter() + .find(|(text, _)| text.starts_with("3D / ORBIT VIEW")) + .expect("viewport information caption"); + assert!( + bounds.right() <= 300.0, + "caption spills past viewport: {bounds:?}" + ); + assert!(bounds.height() > 12.0, "narrow caption should wrap"); + let (_, mesh_bounds) = labels(&output) + .into_iter() + .find(|(text, _)| text.starts_with("Amber: collision wireframe")) + .expect("collision mesh caption"); + assert!( + mesh_bounds.top() > bounds.bottom(), + "caption overlaps mesh note" + ); + assert!( + mesh_bounds.right() <= 300.0, + "mesh note spills past viewport" + ); +} diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index 00886e75..07a162ee 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -322,6 +322,17 @@ impl Viewport { } } } + let caption_color = Color32::from_rgb(115, 144, 171); + let caption = painter.layout( + format!( + "3D / ORBIT VIEW · domain {:.3} m · preview voxel {:.3} mm", + self.domain_side, + self.domain_side * 1000.0 / frame.as_ref().map_or(grid_edge, |f| f.edge) as f64 + ), + egui::FontId::monospace(12.0), + caption_color, + (rect.width() - 28.0).max(1.0), + ); if let Some(mesh) = &self.collision_mesh { const DISPLAY_TRIANGLES: usize = 12_000; let stride = mesh.triangles().len().div_ceil(DISPLAY_TRIANGLES).max(1); @@ -339,16 +350,21 @@ impl Viewport { ); } } - painter.text( - rect.left_top() + egui::vec2(14.0, 38.0), - egui::Align2::LEFT_TOP, + let mesh_color = Color32::from_rgb(245, 180, 65); + let mesh_caption = painter.layout( format!( "Amber: collision wireframe (x-ray), {:?}; showing 1 / {} triangles", mesh.policy(), stride ), egui::FontId::proportional(12.0), - Color32::from_rgb(245, 180, 65), + mesh_color, + (rect.width() - 28.0).max(1.0), + ); + painter.galley( + rect.left_top() + egui::vec2(14.0, 12.0 + caption.size().y + 8.0), + mesh_caption, + mesh_color, ); } self.wire_box( @@ -413,24 +429,20 @@ impl Viewport { ); } } - painter.text( + painter.galley( rect.left_top() + egui::vec2(14.0, 12.0), - egui::Align2::LEFT_TOP, - format!( - "3D / ORBIT VIEW · domain {:.3} m · preview voxel {:.3} mm", - self.domain_side, - self.domain_side * 1000.0 / frame.as_ref().map_or(grid_edge, |f| f.edge) as f64 - ), - egui::FontId::monospace(12.0), - Color32::from_rgb(115, 144, 171), + caption, + caption_color, ); - painter.text( - rect.left_bottom() + egui::vec2(14.0, -12.0), - egui::Align2::LEFT_BOTTOM, - "Drag orbit · Shift pan · Ctrl zoom · Home frame", + let help_color = Color32::from_rgb(142, 158, 176); + let help = painter.layout( + "Drag orbit · Shift pan · Ctrl zoom · Home frame".into(), egui::FontId::proportional(11.0), - Color32::from_rgb(142, 158, 176), + help_color, + (rect.width() - 28.0).max(1.0), ); + let help_origin = rect.left_bottom() + egui::vec2(14.0, -12.0 - help.size().y); + painter.galley(help_origin, help, help_color); } fn input(&mut self, ui: &egui::Ui, response: &egui::Response, rect: Rect, frame_distance: f32) { if !(response.hovered() || response.dragged()) { diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index ec9585d5..4ef3c941 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -1862,7 +1862,7 @@ impl App { self.emitter.pressure_heads=if self.material_mode {[1000.0,800.0]} else {[0.12,0.08]}; } let material_preview = self.material_mode && !configured; - ui.horizontal(|ui| {ui.label(if material_preview {"Adaptive preview edge"} else {"Numerical cells / axis (count mode)"});ui.add_enabled(material_preview || self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.size).range(4..=if material_preview {512}else{rheon::MAX_GRID_EDGE}).suffix("³"));}); + ui.horizontal_wrapped(|ui| {ui.label(if material_preview {"Adaptive preview edge"} else {"Numerical cells / axis (count mode)"});ui.add_enabled(material_preview || self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.size).range(4..=if material_preview {512}else{rheon::MAX_GRID_EDGE}).suffix("³"));}); let mut physical = self.numerical_cells_per_metre.is_some(); if ui.checkbox(&mut physical,"Select physical sampling density").changed() { self.numerical_cells_per_metre = physical.then_some(32); @@ -1886,9 +1886,14 @@ impl App { history::PreviewResolution::Adaptive => {} } ui.small("Preview is display resampling only. Finer voxels replicate appearance; physical sampling stays unchanged."); - ui.horizontal(|ui| {ui.label("Steps");ui.add(egui::DragValue::new(&mut self.steps).range(1..=10000));}); + ui.horizontal_wrapped(|ui| {ui.label("Steps");ui.add(egui::DragValue::new(&mut self.steps).range(1..=10000));}); { - ui.collapsing("Numerical recording (every accepted step)", |ui| { + let recording_label = egui::WidgetText::from("Numerical recording (every accepted step)") + .into_galley(ui, Some(egui::TextWrapMode::Wrap), + (ui.available_width() - ui.spacing().indent - ui.spacing().button_padding.x).max(1.0), + egui::TextStyle::Button); + egui::CollapsingHeader::new(recording_label) + .id_salt("Numerical recording (every accepted step)").show(ui, |ui| { ui.label("Local recording directory"); ui.text_edit_singleline(&mut self.recording_directory); ui.add(egui::DragValue::new(&mut self.recording_database_mib).range(1..=16384).suffix(" MiB database cap")); @@ -1898,7 +1903,7 @@ impl App { }); } ui.small("Carrier inventory: native face velocity, cell appearance tracer, accepted interval pressure. Material inventory: particle attributes and preintegration diagnostics. Other Rheon session/lab backends are not recorded here."); - ui.horizontal(|ui| {ui.label("Timeline budget");ui.add(egui::DragValue::new(&mut self.history_budget_mib).range(16..=4096).suffix(" MiB"));}); + ui.horizontal_wrapped(|ui| {ui.label("Timeline budget");ui.add(egui::DragValue::new(&mut self.history_budget_mib).range(16..=4096).suffix(" MiB"));}); let sampling = if configured { self.selected_carrier_count().map(|size| (size, None)) } else { @@ -1928,9 +1933,9 @@ impl App { settings::quantity(ui,"Maximum step",&mut self.requested_dt,"s",0.001,0.000001..=0.1); ui.small("Accepted time steps adapt to physical stability limits."); if material_preview { - ui.horizontal(|ui| {ui.label("Particle samples / side");ui.add_enabled(self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.materials.config.samples_per_axis).range(4..=64));}); + ui.horizontal_wrapped(|ui| {ui.label("Particle samples / side");ui.add_enabled(self.numerical_cells_per_metre.is_none(),egui::DragValue::new(&mut self.materials.config.samples_per_axis).range(4..=64));}); - ui.horizontal(|ui| {ui.label("Particle limit");ui.add(egui::DragValue::new(&mut self.materials.config.max_particles).range(32768..=262144));}); + ui.horizontal_wrapped(|ui| {ui.label("Particle limit");ui.add(egui::DragValue::new(&mut self.materials.config.max_particles).range(32768..=262144));}); settings::quantity(ui,"Liquid preview stiffness",&mut self.materials.config.liquid_sound_speed,"m/s",1.0,1.0..=100.0).on_hover_text("Reduced acoustic speed for a weakly compressible preview; higher stiffness needs shorter steps. Gas pressure uses the ideal-gas equation."); } else { egui::ComboBox::from_id_salt("implementation").selected_text(self.selected.label()).width(240.0).show_ui(ui,|ui| { @@ -1941,7 +1946,7 @@ impl App { }); egui::CollapsingHeader::new("CPU workers").default_open(false).show(ui, |ui| { if ui.checkbox(&mut self.automatic_threads,"Automatic hardware detection").changed() {self.refresh_threads(available_threads());} - ui.horizontal(|ui| {ui.add_enabled(!self.automatic_threads,egui::DragValue::new(&mut self.threads).range(1..=self.thread_limit));ui.label(format!("/ {} available",self.thread_limit));}); + ui.horizontal_wrapped(|ui| {ui.add_enabled(!self.automatic_threads,egui::DragValue::new(&mut self.threads).range(1..=self.thread_limit));ui.label(format!("/ {} available",self.thread_limit));}); ui.checkbox(&mut self.show_workers,"Orange worker voxel boxes").on_hover_text("Sampled actual worker positions; diagnostics add calculation overhead."); }); if !configured { @@ -1965,7 +1970,7 @@ impl App { ui.checkbox(&mut self.viewport.touchpad_mode,"Touchpad scroll / two-finger orbit").on_hover_text("Enable when your touchpad reports wheel events. Shift pans; Ctrl zooms. Native pinch is supported."); ui.checkbox(&mut self.viewport.camera.orthographic,"Orthographic"); if ui.button("Frame scene [Home]").clicked() {self.viewport.camera=viewport::OrbitCamera::default();} - ui.horizontal(|ui| {ui.label("Surface threshold");ui.add(egui::Slider::new(&mut self.viewport.threshold,1..=80).show_value(false));}); + ui.horizontal_wrapped(|ui| {ui.label("Surface threshold");ui.add(egui::Slider::new(&mut self.viewport.threshold,1..=80).show_value(false));}); ui.label("Mouse: middle drag orbit, Shift+middle pan, wheel zoom."); ui.label("Touchpad: left drag orbit, Shift+left pan, Ctrl+left zoom. Two-finger orbit, Shift-scroll pan, pinch/Ctrl-scroll zoom."); ui.label("1 front · 3 right · 7 top · 5 perspective/ortho · Home frame"); From a8989e24be3480629a98d9bb6df8c780604fc57f Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 20:41:36 +0000 Subject: [PATCH 03/25] fix(desktop): bound recording and pressure field widths (cherry picked from commit 426fd25d22975e1259f5543c77dc771a6ac646ff) --- docs/plans/ui-refinement/execution-ledger.md | 21 ++++ .../reports/input-width-review.md | 46 ++++++++ src/bin/desktop/setup_ui_tests.rs | 103 ++++++++++++++++++ src/bin/rheon-desktop.rs | 8 +- 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/input-width-review.md diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 6c48631c..4ab52962 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -71,3 +71,24 @@ - Only the serial width-repair write set is admitted. No launch/request/worker, recording, Inspector, scientific or dependency behavior changed. Preserve all branches/artifacts; no publication, merge or cleanup performed. + +## 2026-10-20 — explicit recording/pressure field widths + +- Parent forwarded exact original native locations after `cb06a14`: count row, + recording caption/path field, pressure selector and viewport caption. The + prior repair covered rows/captions but left recording desired width implicit + and the pressure selector minimum fixed at 240px. +- Added explicit available-width recording input and pressure selector minimum. + The unchanged narrow-form test still failed the long SGS label; pinned egui + treats combo width as a minimum. Bound its full selected-label galley to the + actual text budget, preserving label/identity and accessible scientific text. +- Focused coverage uses both pressure options, 240/280px forms and a long retained + directory. It checks total content width plus real pointer focus, keyboard tail + navigation and exact full-path copying, without dispatching launch intent. +- Keep `cb06a14` and all its evidence intact. Its native validation remains owned + by the existing validator; this incremental follow-up needs its own exact-source + native check. No native success, run/cancel or long-status stress is claimed. +- Final checks passed: all eight `ui_setup_` tests, locked affected desktop + compile, strict desktop/test Clippy, formatting and diff whitespace. Logs are + retained as `/tmp/rheon-ui-input-width-{tests,check,clippy,fmt}.log`; the before + and intermediate after logs preserve the deciding overflow failures. diff --git a/docs/plans/ui-refinement/reports/input-width-review.md b/docs/plans/ui-refinement/reports/input-width-review.md new file mode 100644 index 00000000..3cf33243 --- /dev/null +++ b/docs/plans/ui-refinement/reports/input-width-review.md @@ -0,0 +1,46 @@ +# Independent input-width follow-up review + +Reviewed 2026-10-20 against retained prerequisite +`cb06a14b53a8506e82f9b1e9d06b9df22c80ce36`. Scope is the current two-file +follow-up diff only; the prerequisite is not rewritten. No remaining material +source finding in this bounded change. Exact-revision native verification is +still required; this review makes no native repair claim. + +The first proposed ComboBox minimum-width change still failed the unchanged +240px-form SGS case (`232.03` content width versus `224` available). Pinned egui +0.33.3 documents width as a minimum, not a hard selected-text bound. The final +repair additionally lays out the complete selected label at available width +minus button padding and icon space. This resolves the observed source/layout +gap without shortening, truncating or shrinking the scientific method label. + +The recording TextEdit explicitly requests available width. This is the same +single-line buffer/widget path and implicit identity as the original convenience +method; it adds no path restriction or truncation of stored input. Egui normally +clips/scrolls long single-line text already, so this review does not claim that +the path itself caused the original overflow. + +Verified by source inspection: + +- ComboBox retains explicit `implementation` identity, full semantic selected + text, Button typography, every method option and typed selection owner. +- Recording path contents, interaction, limits, warning copy, units and + immutable/worker provenance are unchanged. No execution code is edited. +- The new inert test covers both pressure variants at 240px and 280px form + widths with a long path. It measures complete scroll-content width, preserves + the full path and selection, and never constructs a Worker. The same deciding + assertion failed before and passed after the bounded-label repair in + `/tmp/rheon-ui-input-width-{before,after}.log`. +- The final test-only strengthening was independently read: actual pointer + events focus the clipped path field, Ctrl+End reaches the complete path's + final character, and Ctrl+A/Copy emits the exact original path. This proves + the focused egui tail-navigation/copy path while stored input and method + selection remain unchanged; it does not establish native Tab/focus behavior. +- `git diff --check` passed. The lead owns the final compile/lint/format and + complete focused regression record; no such check is a substitute for native + scaling, focus or appearance evidence. + +No GUI, solver, model, evaluator, corpus or scientific workload was run by this +reviewer. Only this report was authored. Final reviewed source SHA256: + +- `src/bin/rheon-desktop.rs`: `0fe4dc285a610eb5478e3faa97437dcb2323b8a0447efbdbf109f55ef12cb010` +- `src/bin/desktop/setup_ui_tests.rs`: `97916f07d7b4c4350fd64c71beb4b1d4642a2fcb23ee364eb9c5497d32af43d4` diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index da809b2b..b43872e5 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -438,3 +438,106 @@ fn ui_setup_viewport_caption_wraps_at_narrow_scaled_width() { "mesh note spills past viewport" ); } + +#[test] +fn ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width() { + for width in [240.0, 280.0] { + for method in PressureImplementation::ALL { + let ctx = context(); + let size = egui::vec2(600.0, 6000.0); + let mut app = App::default(); + app.setup_workflow = SetupWorkflow::AcceptedSession; + app.selected = method; + let path = format!("/retained/{}/recordings", "long-directory/".repeat(150)); + app.recording_directory = path.clone(); + let mut bounds = None; + let mut paint = |ctx: &egui::Context| { + egui::SidePanel::left("bounded-form") + .exact_width(width) + .resizable(false) + .show(ctx, |ui| { + let form = egui::ScrollArea::vertical().show(ui, |ui| app.controls(ui)); + bounds = Some((form.content_size.x, form.inner_rect.width())); + }); + }; + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], &mut paint); + } + for section in ["Simulation", "Numerical recording (every accepted step)"] { + let output = frame(&ctx, size, vec![], &mut paint); + click(&ctx, size, &output, section, &mut paint); + } + let output = frame(&ctx, size, vec![], &mut paint); + assert!(has(&output, "Local recording directory")); + assert!(has(&output, method.label())); + let path_field = output + .shapes + .iter() + .find_map(|shape| match &shape.shape { + egui::Shape::Text(text) if text.galley.text() == path => { + Some(shape.clip_rect.center()) + } + _ => None, + }) + .expect("rendered recording path field"); + for pressed in [true, false] { + let _ = frame( + &ctx, + size, + vec![ + egui::Event::PointerMoved(path_field), + egui::Event::PointerButton { + pos: path_field, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + &mut paint, + ); + } + let path_id = ctx + .memory(|memory| memory.focused()) + .expect("path has pointer focus"); + let modifiers = egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }; + for key in [egui::Key::End, egui::Key::A] { + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }], + &mut paint, + ); + if key == egui::Key::End { + let state = egui::text_edit::TextEditState::load(&ctx, path_id).unwrap(); + assert_eq!( + state.cursor.char_range().unwrap().primary.index, + path.chars().count() + ); + } + } + let copied = frame(&ctx, size, vec![egui::Event::Copy], &mut paint); + assert!(copied.platform_output.commands.iter().any( + |command| matches!(command, egui::OutputCommand::CopyText(text) if text == &path) + )); + let (content, available) = bounds.unwrap(); + assert!( + content <= available + 1.0, + "form width {width}, method {}: content {content} exceeds {available}", + method.label() + ); + assert_eq!(app.recording_directory, path); + assert_eq!(app.selected, method); + assert!(app.worker.is_none()); + } + } +} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index 4ef3c941..7b0c9288 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -1895,7 +1895,7 @@ impl App { egui::CollapsingHeader::new(recording_label) .id_salt("Numerical recording (every accepted step)").show(ui, |ui| { ui.label("Local recording directory"); - ui.text_edit_singleline(&mut self.recording_directory); + ui.add(egui::TextEdit::singleline(&mut self.recording_directory).desired_width(ui.available_width())); ui.add(egui::DragValue::new(&mut self.recording_database_mib).range(1..=16384).suffix(" MiB database cap")); ui.add(egui::DragValue::new(&mut self.recording_record_mib).range(1..=64).suffix(" MiB per publication")); ui.small("Retention: keep all; stop at limits. Caps apply per track/database; comparisons create separate archives. Record quota = requested steps + constructor. Files persist after reset; delete explicitly outside Rheon."); @@ -1938,7 +1938,11 @@ impl App { ui.horizontal_wrapped(|ui| {ui.label("Particle limit");ui.add(egui::DragValue::new(&mut self.materials.config.max_particles).range(32768..=262144));}); settings::quantity(ui,"Liquid preview stiffness",&mut self.materials.config.liquid_sound_speed,"m/s",1.0,1.0..=100.0).on_hover_text("Reduced acoustic speed for a weakly compressible preview; higher stiffness needs shorter steps. Gas pressure uses the ideal-gas equation."); } else { - egui::ComboBox::from_id_salt("implementation").selected_text(self.selected.label()).width(240.0).show_ui(ui,|ui| { + let pressure_label = egui::WidgetText::from(self.selected.label()).into_galley( + ui, Some(egui::TextWrapMode::Wrap), + (ui.available_width() - 2.0 * ui.spacing().button_padding.x - ui.spacing().icon_spacing - ui.spacing().icon_width).max(1.0), + egui::TextStyle::Button); + egui::ComboBox::from_id_salt("implementation").selected_text(pressure_label).width(ui.available_width().min(240.0)).show_ui(ui,|ui| { for method in PressureImplementation::ALL {ui.selectable_value(&mut self.selected,method,method.label());} }); ui.checkbox(&mut self.tight,"Tight pressure tolerance"); From ed79eddaa030eba142d6ba5ecb4f62ff0d932cfb Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 21:07:37 +0000 Subject: [PATCH 04/25] test(desktop): cover synthetic active UI and record scoped native evidence (cherry picked from commit e1097581d751c061eaf8c2df45d83de62911a74d) --- docs/plans/ui-refinement/execution-ledger.md | 33 +++++ docs/plans/ui-refinement/issues.md | 8 +- docs/plans/ui-refinement/plan.md | 33 ++++- .../reports/m1-fixture-review.md | 63 ++++++++++ .../reports/m1-native-and-gap-handoff.md | 113 ++++++++++++++++++ src/bin/desktop/setup_ui_tests.rs | 104 ++++++++++++++++ 6 files changed, 347 insertions(+), 7 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/m1-fixture-review.md create mode 100644 docs/plans/ui-refinement/reports/m1-native-and-gap-handoff.md diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 4ab52962..9a98045f 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -92,3 +92,36 @@ compile, strict desktop/test Clippy, formatting and diff whitespace. Logs are retained as `/tmp/rheon-ui-input-width-{tests,check,clippy,fmt}.log`; the before and intermediate after logs preserve the deciding overflow failures. + +## 2026-10-20 — scoped native receipt and remaining M1 evidence + +- Parent reports existing validator confirmed exact `426fd25` and both transfer + packets/parent/commit/tree hashes; developer build passed in 110.6s. Native + repaired forms/HUD/collision note, both pressure options/dropdowns, scrolling, + retained Steps, exact 674-character directory copy in all four size/scale + cases, close/reopen passed; both sessions exited 0. +- Forty-two actual JPEGs retained by validator (quality 85 requested; image + metadata not supplied to this task); delivery Forbidden, + no confirmed Library IDs. One capture completed decodable files before its + completion command timed out; it was not repeated. Parent reports 483 prior + evidence files and three original databases unchanged. This task did not + inspect/re-hash those remote native artifacts and performs no delivery retry. +- Plan/issue state now distinguishes observed idle success from A4's remaining + active-progress/Cancel/frozen-rerun, long-status and accessibility gaps. A5's + counts/user-timing are pending; whole M1 acceptance remains incomplete. +- Inspected existing deterministic fixtures. `ProgressGate` tests call actual + Worker::start/solver/recording paths and were not run. Added only an inert + sidebar test using the existing drop-worker owned-handle pattern: one waiting + synthetic thread, manual progress/timing and empty 16 MiB-budget history. + It tests frozen request projection and actual Cancel flag routing; the handle + ignores cancellation until explicit test release. No genuine worker retirement, + numerical progress, archive publication or native behavior is implied. +- Initial compilation needed a test-only result assertion adjustment because + ResultRow lacks Debug; no production derive or behavior change was introduced. + [Scoped receipt/gap handoff](reports/m1-native-and-gap-handoff.md) specifies a + future genuine-worker proposal and exact request/component caps. It explicitly + requires separate live-execution authority; it is not executed here. +- Final source/test gate: nine `ui_setup_` tests passed, strict + affected desktop/test Clippy, locked desktop compile and formatting passed. + Logs remain `/tmp/rheon-ui-m1-fixture-{tests,clippy,check,fmt}.log`. This is + synthetic/headless evidence only; no actual worker or native gate was run. diff --git a/docs/plans/ui-refinement/issues.md b/docs/plans/ui-refinement/issues.md index 6a7b7804..ec41a0c6 100644 --- a/docs/plans/ui-refinement/issues.md +++ b/docs/plans/ui-refinement/issues.md @@ -2,7 +2,7 @@ | ID | Finding / owner | Disposition / revisit | | --- | --- | --- | -| UI-1 | No native display/session; UI task + existing native validator | Required native acceptance blocked. Execute exact handoff after local source/test checkpoint; do not substitute browser/headless claims. | +| UI-1 | Local display absent; native owner reports repaired idle UI pass at exact `426fd25` | A4 remains blocked for active progress/Cancel/frozen rerun, native long-status and broader focus/accessibility evidence. See scoped native receipt/gap handoff. | | UI-2 | No application standards-engine tool; UI lead | Current pinned standards read directly; no automated conformance certification. Revisit if authoritative application tool becomes available. | | UI-3 | Separate local Inspector slice/dependency and test-only Clippy fix; Inspector validator | Excluded from M1. Reconcile final validated source before M4; parent relays coordination. | | UI-4 | 40% visible-word/usability target needs current matched native baseline; native owner | No result claimed. Preserve warning/numerical context regardless of count; evaluate full staged refinement. | @@ -17,3 +17,9 @@ idle workflows; expanded form and viewport caption horizontal clipping found. Width repair implemented with focused regressions; exact repaired revision awaits native validation. Active progress/Cancel/frozen rerun and long-status stress are still open evidence gaps. See the width-repair source review and latest ledger. + +Latest native update supersedes the earlier idle-width pending note: parent +reports final `426fd25` repaired forms/HUD/mesh note and idle interactions passed +at both sizes/scales. Forty-two actual JPEGs retained; Forbidden delivery has no +confirmed Library IDs. One capture completion command timed out after decodable +files were produced; it was not repeated. Remaining acceptance gaps stay open. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 21082b5a..bf994297 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Focused desktop UI refinement **Plan status:** `Active` -**Current phase:** M1 native clipping repair (exact repair awaits native verification) -**Next slice:** M1 — validate the width repair in the existing native environment +**Current phase:** M1 partial native evidence and remaining fixture checks +**Next slice:** M1 — workload-free active-state checks; bound remaining native/live evidence **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -108,7 +108,7 @@ may change throughout. Generated logs/renders remain outside Git per | A1 | Actual egui pointer selection presents only the selected setup form and one primary launch; switching preserves settings and never launches work | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | | A2 | Primary action/status remain outside scroll content, including Inspector mode; shared consumed inputs remain reachable; disabled package launch remains explicit | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | | A3 | Launch/request/worker/cancel/recording semantics unchanged; no Inspector/D1/numerical edits; unit and limitation copy retained | focused / not-applicable / manual source review | satisfied | [Verification](reports/verification.md) | -| A4 | Native 1250×820 and 900×650 at 100% and 150% UI scale: no clipped labels/overlap; mode/action/status visible; scroll/keyboard/focus/help and in-progress cancellation work | user-workflow / required-real supported native display / manual | blocked | No display/session here | +| A4 | Native 1250×820 and 900×650 at 100% and 150% UI scale: no clipped labels/overlap; mode/action/status visible; scroll/keyboard/focus/help and in-progress cancellation work | user-workflow / required-real supported native display / manual | blocked | [Scoped native receipt and remaining gaps](reports/m1-native-and-gap-handoff.md); idle repaired UI passed per parent, active/native accessibility gaps remain | | A5 | Matched current before/after states show materially less text with unchanged values/warnings; target 40% fewer visible explanatory words across full planned refinement, not M1 alone; 4/5 users identify workflow/edit material/find launch within 30s | user-workflow / representative native / manual | pending | No counts/usability results yet | | A6 | Rust 1.92 formatting, affected desktop build, strict affected-target Clippy and focused UI tests pass | focused / Linux pinned Rust / automated | satisfied | [Verification](reports/verification.md) | @@ -161,14 +161,17 @@ lint/test failures inside the admitted boundary without restarting design. ## Blockers -Native display and representative user evidence are unavailable, so objective -acceptance remains incomplete. No standards application MCP is exposed; direct +Local native display remains unavailable. The existing validator has reported +scoped idle native success at exact `426fd25`; active-worker, native long-status, +accessibility and representative user evidence remain incomplete. JPEG delivery +is blocked separately (Forbidden; no confirmed Library IDs). No standards application MCP is exposed; direct source standards review is used and no engine conformance certificate claimed. Build/tool availability and any baseline lint failures are recorded in the ledger. ## Final Acceptance -Native visual/interaction and matched-state usability claims remain unsatisfied. +Scoped idle native visual/interaction evidence is recorded; complete native +active-worker/accessibility and matched-state usability acceptance remain unsatisfied. No delivery, standards-engine certification or whole-plan acceptance is claimed. ## M1 native-feedback repair admission — 2026-10-20 @@ -191,3 +194,21 @@ exercise only under the previously bounded fixture/run authority. Parent reports 30 retained native JPEGs of the prerequisite; no images or native verification of this repair are claimed here. Roll back only the isolated repair commit if needed; preserve the accepted prerequisite and unrelated Inspector work. + +## Scoped native checkpoint and remaining M1 admission — 2026-10-20 + +Parent reports exact `426fd25d22975e1259f5543c77dc771a6ac646ff` and both +incremental transfer/parent/tree hashes validated in the existing native owner. +The repaired idle workflows passed at both supported sizes/scales. This closes +those clipping observations only; A4 remains blocked on its unobserved parts and +A5 remains pending. See [the scoped receipt/gap handoff](reports/m1-native-and-gap-handoff.md). + +Continue M1 with test-only synthetic active-state coverage in +`src/bin/desktop/setup_ui_tests.rs`; no production changes or new runtime fixture +flags. Reuse the existing inert Worker-handle pattern, empty budgeted history and +manual progress atoms. Verify actual sidebar Cancel routing/frozen-request +projection without `Worker::start`, solver calls or recording publication. This +is mocked UI-state evidence, not worker cancellation or native verification. +Propose precise bounds for any actual worker test; do not execute it under this +admission. No M2–M4 expansion, changed resource budget, model/scientific run, +publication, cleanup or retry of Forbidden image delivery is admitted. diff --git a/docs/plans/ui-refinement/reports/m1-fixture-review.md b/docs/plans/ui-refinement/reports/m1-fixture-review.md new file mode 100644 index 00000000..d37edd3f --- /dev/null +++ b/docs/plans/ui-refinement/reports/m1-fixture-review.md @@ -0,0 +1,63 @@ +# Independent M1 fixture and gap-handoff review + +Reviewed 2026-10-20 against production checkpoint +`426fd25d22975e1259f5543c77dc771a6ac646ff`. Scope: the new test-only synthetic +active-state case and `m1-native-and-gap-handoff.md`. Only this report was +authored; no numerical, model, native or scientific workload was executed by +the reviewer. + +## Fixture disposition + +No workload path or remaining material test-source issue identified. The test +constructs one waiting owned thread, manually initialized progress/cancel atoms, +an inert Request and empty bounded LiveHistory. `with_budget` reserves empty +frame slots, `begin_step` only records timing, and `CpuScheduler::new` initializes +accounting state. The path paints App's real sidebar and dispatches the actual +Cancel operation with `child=None`. It never calls `Worker::start`, `run_owned`, +physics advancement, model startup, frame publication, archive open or recording. + +The five-second channel timeout bounds the synthetic thread's lifetime even +if an assertion prevents explicit release. On success the test releases and +joins it. The thread intentionally ignores the cancel flag; asserting it stays +alive after Cancel prevents confusing signal routing with worker retirement. +The synthetic outcome is consumed by the test, not polled into a fabricated App +completion status. No production flag, state machine or numerical default changes. + +At both supported logical sizes, the test checks manually seeded 1/4 progress, +frozen request values despite conflicting next-run preferences, screen-contained +Cancel/progress text and Cancel's stable position through Inspector switching. +It then proves real flag routing and unchanged progress/preferences. These are +egui input/projection claims, not measured physical progress, real saved-rerun +execution, cancellation latency/cleanup, native scaling or focus evidence. + +The final lead log records nine `ui_setup_` tests passing and final static gates +passing. Reviewed test SHA256: +`1df47b37d2a830617ef2c6a2389a512f57c490a400b745b5927379501419129a`. +No production file differs from the prerequisite in this fixture slice. + +## Handoff evidence boundary + +The handoff attributes native idle success and 42 retained JPEGs to +the parent's validator relay, binds production checkpoint/tree and explicitly +states that this task did not view/re-hash those artifacts. Forbidden delivery +is not treated as a confirmed Library save. The uncertain capture completion is +not retried or mislabeled as capture failure. Idle native results do not close +active-state, long-status, focus/accessibility, user timing or word-count claims. +Quality-85 export was requested; missing capture metadata is explicitly not +represented as independent confirmation of the image quality setting. + +The possible native UI-only harness remains a proposal: test-only entry point, +clear fixture label, synthetic values, no production CLI flag and no launch in +this turn. The separate genuine-worker proposal explicitly calls out numerical +execution/new recording, existing owner limits versus RSS, fresh paths, no model, +no budget increases, no retry and separate execution/resource authority. A frozen +saved-rerun needs its own authorized immutable fixture and bounds. + +The requested timeout clarification is resolved and independently re-read: +the proposed 10-second gate-arrival and 10-second retirement waits bound +observations, not the full process. The handoff now explains the potentially +blocking destructor join, cancellation's limits, the need for separately +authorized supervision/teardown when a hard process limit is required, and the +absence of process-kill authority. This does not affect the current five-second +synthetic handle. No remaining material source or handoff-boundary finding in +this scoped fixture checkpoint; whole M1 acceptance remains incomplete. diff --git a/docs/plans/ui-refinement/reports/m1-native-and-gap-handoff.md b/docs/plans/ui-refinement/reports/m1-native-and-gap-handoff.md new file mode 100644 index 00000000..11724ce7 --- /dev/null +++ b/docs/plans/ui-refinement/reports/m1-native-and-gap-handoff.md @@ -0,0 +1,113 @@ +# M1 scoped native receipt and bounded remaining evidence + +## Receipt and custody + +Source of this receipt: parent relayed the existing native validator's results +on 2026-10-20. This UI task did not independently view those captures or run that +native session. Exact inspected production checkpoint: +`426fd25d22975e1259f5543c77dc771a6ac646ff`, tree +`52a4f717e1175562bbe438c37392762e5b18067e`. Parent confirms both incremental +transfer packets plus parent/commit/tree hashes, and a developer build passing +in 110.6 seconds. This is a developer-build receipt, not hermetic qualification. + +At 1250×820 and 900×650, 100% and 150%, parent reports passing wrapped expanded +forms/HUD/collision note, both pressure selections/dropdowns, independent scroll, +retained Steps, exact copying of a 674-character directory in all four cases, +and close/reopen. Both native sessions exited 0. Forty-two actual JPEGs were +retained; quality-85 export was requested, but capture metadata was not supplied +to this task for independent confirmation. Delivery returned Forbidden; no Library identities were +confirmed. Do not retry delivery here or label historical captures as delivered. +One capture produced decodable files before its completion command timed out; +it was not repeated. Do not treat that timeout as evidence the capture failed or +as an automatic retry instruction. Parent reports all 483 prior evidence files +and three original databases unchanged; this task did not re-hash that custody. + +This receipt covers those observed idle states only. It does not prove active +progress/Cancel/frozen rerun, long-status stress, full keyboard/accessibility, +user-task timing, a 40% matched-state word reduction, scientific correctness or +complete M1 acceptance. No current screenshot IDs or image hashes were supplied +to this UI task, so image delivery/readability beyond the relay is unverified. + +## Remaining acceptance and smallest independent checks + +| Gap | Existing evidence / route | Honest boundary | +| --- | --- | --- | +| Active strip/progress and frozen actual request | Existing inert `ui_setup_running_summary_uses_frozen_physical_samples_and_timestep`; new synthetic active Worker-handle sidebar test | Manual atoms and request snapshots test UI projection only; not live progress or a genuine saved-rerun execution | +| Cancel reachability and signal routing | New synthetic owned handle; pointer click Cancel after switching Inspector; shared actual atomic cancel owner | Verifies App/sidebar/Worker cancel-flag routing. Fixture ignores that flag until explicit release, so it deliberately cannot prove solver retirement/cancel latency | +| Long full status | Existing `ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector` with >2700-character path, Ctrl+End/select/copy/edit refusal | Headless test requests focus directly; native long-status stress and ordinary Tab discovery are still open | +| Native focus/help and usability | Existing validator covers selected native copy/edit interactions | No complete Tab/Shift+Tab, assistive-technology, 4/5 user timing or word-count claim | +| Genuine worker cancellation | Existing `cancel_after_progress_joins_without_rows_and_allows_restart` and `ProgressGate` | Calls `Worker::start` and advances numerics. Not run in this UI task; existing restart/comparison/default-budget scope is larger than needed | + +The synthetic test borrows the pattern from +`dropping_app_cancels_and_joins_owned_worker`: one waiting test thread, one +sync-channel message, empty `LiveHistory::with_budget(4,4,1,16 MiB)`, manual +progress 1/4 and timing step 2. One thread exists at a time, with a five-second +receive timeout as a teardown bound; it does not spin or run a solver. No +`Worker::start`, `run_owned`, child/model, publication, archive open or file write +is called. Inspector/default constructors hold paths but open no recording. +Retained form uses Accepted Session/99 steps/1 ms while the frozen inert request +uses carrier preview/4 samples per axis/2 m/4 steps/13 ms. A visible Preview label +and frozen numbers must come from the request, not the next-run form. Numeric +values are synthetic projections, never simulated measurements. Cancel must set +the shared flag while the handle remains waiting; explicit fixture release then +joins without polling a fabricated simulation outcome into App. + +A possible native UI-only harness could reuse this same synthetic snapshot and +full long-status string, with a clear permanent fixture label. It would require +its own test-only native entry point and owner coordination; none is added or +launched here. That could assess rendered active layout, Tab focus, copy and +Cancel affordance with zero numerical work, while still leaving genuine worker +cancellation unproven. Avoid adding production CLI switches solely for this. + +## Proposed genuine-worker check — not executed or authorized here + +If genuine cancellation is still required, the smallest proposed automated +follow-up reuses the existing test-only `ProgressGate` in a new focused case, +rather than running the broad existing cancellation-and-restart test unchanged: + +- One ordinary carrier track; `JacobiPcgV1`; grid 4×4×4, domain 1 m, origin zero, + requested dt 0.02 s, requested steps 2, threads 1, tight=false, observe=false. + material/emitter/collision/configured/saved-set/child absent. Ordinary carrier + source follows the unchanged desktop path; do not claim zero physics work. +- Explicit preview budget 16 MiB, edge 4, one track, at most three frame slots. + Existing carrier owner retains its hardcoded 192 MiB numerical-array limit; + pressure max_iterations remains 2000 per solve. These are component limits, + not a process RSS cap or a guarantee of exact work/latency. +- Proposed recording caps: 8 MiB database, 1 MiB publication, max_records=3 + (constructor plus two requested steps). One fresh isolated temporary recording + directory; no paths to the three preserved databases. Rollback journal may + add approximately one database plus page/sector overhead; allocator, SQLite, + GUI and thread overhead are outside these caps. All proposals are no greater + than existing test defaults; do not increase any budget to make a test pass. +- Attach a single `ProgressGate`; wait at most the existing 10 seconds for the + first acknowledged accepted step, assert progress=1 and live handle. Route + Cancel through the actual sidebar, then release the gate. Wait at most 10 more + seconds for retirement; record an observation failure and retain artifacts on + timeout, with no retry. These two waits are observation deadlines, not a 20s + hard process bound: panic/timeout can reach App::drop's blocking worker join, + and cancellation need not interrupt every stalled native operation. Before + live execution, obtain a separately authorized supervisor/teardown disposition + if a hard process limit is required; this proposal does not grant process-kill + authority. Use no unapproved termination or settings changes to enforce it. +- Expected evidence: exact source/build/test identity and full request/caps; + gate arrival, progress 1/2, actual cancel flag, joined handle, no completed + success rows, retained accepted prefix, reader verification and distinct + terminal cancellation versus recording-completeness status. Genuine physical + cancellation must not be inferred from the flag alone. Preserve generated + archive/logs on failure; do not edit old evidence or run the restart branch. + +This would run a numerical solver and write a new recording. It therefore +requires separate explicit live-execution authority and the validator's bounded +resource approval; no such execution is part of this turn. Frozen saved-rerun +execution would additionally need an approved immutable existing fixture plus +its own declared run/recording bounds; do not manufacture one by silently running +a producer or model. A synthetic frozen request can test projection meanwhile. + +## Native continuation + +Sequence through the existing native owner, after its current task. For any +UI-only continuation retain exact revision/build/state/scale labels and actual +quality-85 JPEGs, with confirmed Library identity only after successful delivery. +Do not repeat the Forbidden upload or timed-out capture automatically. Keep all +previous branches, packets, logs, images and databases intact. M1 stays Verifying; +M2–M4 stay Planned and A5 remains pending. diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index b43872e5..1ccae772 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -541,3 +541,107 @@ fn ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width() { } } } + +#[test] +fn ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal() { + // Reuse the inert owned-handle pattern from dropping_app_cancels_and_joins_owned_worker. + // The handle waits for explicit test release, not for cancellation. Nothing calls + // Worker::start/run_owned, publishes frames, opens recordings or advances physics. + for size in [egui::vec2(900.0, 650.0), egui::vec2(1250.0, 820.0)] { + let ctx = context(); + let cancel = Arc::new(AtomicBool::new(false)); + let progress = Arc::new(AtomicUsize::new(1)); + let (release, wait) = std::sync::mpsc::sync_channel(1); + let handle = std::thread::spawn(move || { + let _ = wait.recv_timeout(Duration::from_secs(5)); + Err("Synthetic UI fixture released; no simulation executed".into()) + }); + let history = LiveHistory::with_budget(4, 4, 1, 16 * 1024 * 1024).unwrap(); + history.begin_step(0, 2).unwrap(); + let mut app = App::default(); + app.setup_workflow = SetupWorkflow::AcceptedSession; + app.steps = 99; + app.requested_dt = 0.001; + app.running_request = Some(Request { + collision_mesh: None, + size: 4, + steps: 4, + tight: false, + threads: 1, + methods: vec![PressureImplementation::default()], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Adaptive, + domain_side: 2.0, + requested_dt: 0.013, + material: None, + material_color: [0; 3], + recording_limits: Default::default(), + recording_directory: String::new(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + }); + app.worker = Some(Worker { + scheduler: rheon::CpuScheduler::new(), + cancel: Arc::clone(&cancel), + progress: Arc::clone(&progress), + total: 4, + history, + observer: None, + child: None, + handle, + }); + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + } + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(has(&output, "1/4 steps")); + assert!(has(&output, "Simulation preview")); + assert!(has(&output, "4 steps · maximum step 13.000 ms")); + assert!(has(&output, "4 samples/axis · domain 2.000 m")); + assert!(!has(&output, "Simulate") && !has(&output, "Save & run native")); + let cancel_rect = rect(&output, "Cancel simulation"); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, size); + assert!(screen.contains_rect(cancel_rect)); + assert!(screen.contains_rect(rect(&output, "1/4 steps"))); + assert!(cancel_rect.right() <= 280.0); + click(&ctx, size, &output, "Inspector", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(has(&output, "Numerical inspector")); + assert_eq!(cancel_rect, rect(&output, "Cancel simulation")); + click(&ctx, size, &output, "Cancel simulation", |ctx| { + app.sidebar(ctx); + }); + assert!(cancel.load(Ordering::Relaxed)); + assert_eq!(progress.load(Ordering::Relaxed), 1); + assert!( + !app.worker.as_ref().unwrap().handle.is_finished(), + "Cancel routes a signal; this fixture does not simulate worker retirement" + ); + assert_eq!((app.steps, app.requested_dt), (99, 0.001)); + release.send(()).unwrap(); + let worker = app.worker.take().unwrap(); + assert!( + worker + .handle + .join() + .unwrap() + .err() + .unwrap() + .contains("Synthetic UI fixture") + ); + } +} From 056643f792b8c491ef8ca15e3b7ce316e384c5e4 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 21:30:50 +0000 Subject: [PATCH 05/25] test(desktop): define supervised cancellation contract and stress status UI (cherry picked from commit 7318023f04946171d7909119300af76e455c2926) --- docs/plans/ui-refinement/execution-ledger.md | 42 ++ docs/plans/ui-refinement/plan.md | 25 +- .../reports/m1-cancellation-contract.md | 150 ++++++ .../reports/m1-cancellation-review.md | 104 ++++ src/bin/desktop/setup_ui_tests.rs | 439 ++++++++++++---- tools/supervise_ui_cancel.py | 484 ++++++++++++++++++ tools/test_supervise_ui_cancel.py | 276 ++++++++++ 7 files changed, 1410 insertions(+), 110 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/m1-cancellation-contract.md create mode 100644 docs/plans/ui-refinement/reports/m1-cancellation-review.md create mode 100644 tools/supervise_ui_cancel.py create mode 100644 tools/test_supervise_ui_cancel.py diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 9a98045f..034ed509 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -125,3 +125,45 @@ affected desktop/test Clippy, locked desktop compile and formatting passed. Logs remain `/tmp/rheon-ui-m1-fixture-{tests,clippy,check,fmt}.log`. This is synthetic/headless evidence only; no actual worker or native gate was run. + +## 2026-10-20 — bounded cancellation contract, no live execution + +- Preserved local `implementation/ui-hierarchy-20261020` at e109758 and all old + artifacts. Parent reports its remote now426fd25 after separate direct user + interaction. Created only local continuation branch + `implementation/ui-m1-cancel-contract-20261020`; no remote mutation or fetch. +- Added Linux external supervisor for a sealed exact ignored test. It requires + preconfigured exclusively reserved cgroup2/tmpfs, kernel resource bounds and + conservative bounded TERM/KILL/reap disposition. No fallback, mounts, budget + expansion, credential/network changes or cleanup. Read-only preflight would + refuse ordinary/shared filesystems or cgroup root; no resource leaf is + provisioned or admitted in this turn. +- Added ignored genuine cancellation test, excluded from ui_setup_ and not run. + It specifies actual first-step gate, pointer Cancel while gate held, nonblocking + UI painting, completed-handle join and verified distinct cancellation/prefix + recording evidence. Existing worker/numerical production code is unchanged. +- Expanded inert status cases to long path,16KiB unbroken text and multiline + Unicode, full copy/end/edit refusal, pointer focus and Shift+Tab exit. Initial + stress fixture reused UI state across cases and used a text clip rather than + actual interact rectangle for clicks; corrected those test-only assumptions, + preserving the bounded-height oracle. Nine focused tests pass (new genuine + test remains ignored and filtered). +- Supervisor unit fixtures use no solver/model/native workload. They include + an explicitly owned waiting process for TERM/KILL/reap, immutable memfd bytes, + kernel pipe capacity, cap/refusal/result checks and injected cleanup failures. + Fixture artifacts remain /tmp/rheon-ui-supervisor-tests-*; nothing cleaned. + Mocked cgroup/volume checks are not a kernel-containment integration pass. +- Linux headers verified seal/pipe UAPI constants when this Python build lacked + named fcntl constants. Actual memfd seals and pipe-size tests pass. No alternative + upload/HTTP route, account, sandbox bypass or system reconfiguration was used. +- Independent review caught and resolved teardown edge cases: descriptor ownership + during signals, old absolute deadlines, stricter inherited rlimits, ECHILD/PID + reuse suppression, short receipt writes and close-error acceptance revocation. + The owned waiting-process unit cleanup now retains pidfd through its final + signal/reap; no post-reap numeric-PID fallback remains. +- Final inert/static gates: nine Rust UI tests pass; thirteen Python supervisor + unit tests pass normally and with -O; strict desktop/test Clippy, locked desktop + compile, Rust formatting and diff whitespace checks pass. Logs retained under + /tmp/rheon-ui-contract-* and /tmp/rheon-ui-supervisor-tests*.log. These do not + constitute genuine cancellation, kernel cgroup/tmpfs integration or native + accessibility verification. Existing native result remains scoped to426fd25. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index bf994297..39753fae 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -2,7 +2,7 @@ **Plan status:** `Active` **Current phase:** M1 partial native evidence and remaining fixture checks -**Next slice:** M1 — workload-free active-state checks; bound remaining native/live evidence +**Next slice:** M1 — reviewed supervisor contract; genuine execution remains unadmitted **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -27,7 +27,10 @@ Base remote main revalidated 2026-10-20: `fd7c0f0aee63bc8710df99f9bb01154e7619b974`. Local branch `implementation/ui-hierarchy-20261020`, worktree `/workspace/Rheon-ui-hierarchy`. UI task owns implementation and serial integration; parent owns eventual main -integration. Branch is private, retained for review/validation. No push, PR, +integration. That branch is retained for review/validation. Parent later reports +its remote at `426fd25` after separate user action; do not overwrite it. Current +local continuation from `e109758` uses `implementation/ui-m1-cancel-contract-20261020`. +No push, PR, merge, rebase, branch deletion or cleanup is authorized in this turn. ## Scope and non-goals @@ -212,3 +215,21 @@ is mocked UI-state evidence, not worker cancellation or native verification. Propose precise bounds for any actual worker test; do not execute it under this admission. No M2–M4 expansion, changed resource budget, model/scientific run, publication, cleanup or retry of Forbidden image delivery is admitted. + +## M1 cancellation-contract admission — 2026-10-20 + +Parent requests a complete external supervised cancellation contract, not live +execution. Serial write set: `src/bin/desktop/setup_ui_tests.rs` (ignored genuine +case plus inert long-status stress), new `tools/supervise_ui_cancel.py` and its +unit tests, plus this plan directory. No application production code changes. +See [reviewed cancellation contract](reports/m1-cancellation-contract.md). + +Keep genuine execution ignored/unadmitted. Require exact source/binary seals, +externally reserved preconfigured cgroup and bounded output tmpfs; fail before GO +if prerequisites are unavailable. No fallback/provisioning/settings changes. +The hard resource/disposition contract must distinguish CPU bandwidth, per-process +limits, observed aggregate trips, kernel allocated storage, and unresolved kernel +teardown. Only disposable owned test processes/descendants can be terminated. +Independent source review and non-numerical tests precede any future admission. +Long-status headless stress can proceed now; native accessibility, genuine worker +cancellation and whole M1 acceptance remain open. M2–M4 stay Planned. diff --git a/docs/plans/ui-refinement/reports/m1-cancellation-contract.md b/docs/plans/ui-refinement/reports/m1-cancellation-contract.md new file mode 100644 index 00000000..0575a31b --- /dev/null +++ b/docs/plans/ui-refinement/reports/m1-cancellation-contract.md @@ -0,0 +1,150 @@ +# M1 bounded cancellation contract — no live admission + +The implementation is a **conditional, unexecuted genuine-test contract**, not a +cancellation result. Production UI is unchanged from native-validated `426fd25`. +This continuation starts at `e1097581d751c061eaf8c2df45d83de62911a74d` on new local +branch `implementation/ui-m1-cancel-contract-20261020`. The original local branch +is preserved; parent reports remote `implementation/ui-hierarchy-20261020` now +points to `426fd25` after separate user action. No fetch/push/reset/rebase occurs. + +## Exact execution and identity + +Only `setup_ui_tests::bounded_genuine_cancel_contract` may execute, via +`tools/supervise_ui_cancel.py`. It is `#[ignore]`, excluded from `ui_setup_`, and +requires runtime admission/root plus a compiled source revision before +`Worker::start`. Direct `cargo test --ignored` or invented environment values are +not an admitted execution route. The supervisor flag is acknowledgement of a +separate authorization, not authorization by itself. No numerical/model test was +run in this turn; no runtime provisioning or global settings were changed. + +After this checkpoint is committed, use its exact full commit/tree and clean +tracked source. Build with existing trusted Rust/Cargo 1.92.0, locked dependencies, +explicit existing tool homes and a fresh target outside every checkout. Set +`RHEON_SOURCE_REVISION` to that exact commit and use only: +`cargo test --locked --features desktop --bin rheon-desktop --no-run --message-format=json`. +Record the actual emitted executable, complete build log/tool versions and +Cargo.lock SHA256. This builds tests without running them. The ordinary qualified +application-binary sidecar does not automatically qualify a test binary. + +Create an external JSON seal with fields `test` (exact name above), `toolchain` +(`1.92.0`), full `commit`, full `tree`, `cargo_lock_sha256`, absolute `binary`, +`binary_bytes`, `binary_sha256`, and `supervisor_sha256` (exact script bytes). +The supervisor compares clean git identity/lock/script, hashes and copies the +actual ELF into a write/grow/shrink/seal-locked memfd, then executes those exact +immutable bytes, not a re-resolved path. Input <=512 MiB; growth during copy is +refused. The memfd allocation belongs to the supervisor before child admission; +its <=512 MiB input memory is **outside** the child's memory cgroup limit. +The compiled revision is a caller-supplied declaration checked by the test, +not hermetic build attestation. Preserve the original sealed build artifact/log. + +## Mandatory preconfigured resources — fail before GO if unavailable + +The execution owner must already have separate authority/custody for: + +| Boundary | Enforced contract | +| --- | --- | +| Process subtree | Genuine cgroup-v2 dedicated leaf, initially empty, no child groups, excludes supervisor; write rights to `cgroup.procs`/`cgroup.kill`; at GO membership exactly the owned waiting PID | +| Exclusive custody | Leaf and output filesystem reserved externally for this supervisor's entire lifetime; no concurrent admission, reconfiguration, remount or other users' work. Empty-at-preflight is not a lease | +| CPU | Preconfigured `cpu.max=10000 10000`, `cpu.max.burst=0`: at most one CPU-equivalent bandwidth. Inherited per-process RLIMIT_CPU soft/hard at most5/6 s. Aggregate `cpu.stat` 5s is a **monitored trip**, not an exact hard cumulative CPU budget | +| Memory/processes | Preconfigured `memory.max` <=512 MiB, `memory.swap.max=0`, `pids.max` <=16 including threads. Inherited RLIMIT_AS at most1 GiB/process, NOFILE at most64, CORE0 | +| Individual file | Inherited RLIMIT_FSIZE at most8 MiB/process | +| Aggregate retained storage | Empty dedicated tmpfs mount root, `noswap`, kernel capacity <=32 MiB, <=1024 inodes. Includes logs, receipts, TMPDIR, SQLite database/journal; no shared-filesystem free-space substitute. This bounds **allocated storage**, not sparse logical lengths or cumulative rewritten bytes | +| Captured output | Each stdout/stderr file retains <=1 MiB (2 MiB combined). Each pipe is kernel-sized4096 bytes; read at most remaining+1 then close on overflow, with no continued drain during grace. Observed counts retained; trusted test never resizes pipes. OS-accepted pipe bytes are bounded by 2 MiB +8194 bytes across both pipes, not arbitrary application strings constructed in memory | +| Wall/disposition | Start clock before fork; TERM by 24s observation, at most 1s grace, subtree KILL at 25s deadline; monitor polls20ms. These are supervisor kill-disposition deadlines subject to OS scheduling, not a guarantee a kernel D-state task retires by25s | + +Stricter inherited rlimits are retained, never raised; effective values are +recorded in the receipt. Such stricter limits may refuse/fail the test without +any budget increase. + +Linux semantics: [cgroup v2](https://docs.kernel.org/admin-guide/cgroup-v2.html) +defines bandwidth/memory/subtree control; +[getrlimit](https://man7.org/linux/man-pages/man2/getrlimit.2.html) defines inherited +per-process limits; [tmpfs](https://docs.kernel.org/filesystems/tmpfs.html) defines +allocated capacity/inode/noswap bounds. No claim that polling disk sizes or +per-file limits alone enforce a hard aggregate quota. + +This is containment of a trusted pinned test with reviewed output paths, not a +hostile-code/filesystem/network sandbox. Cgroup kill includes detached process +groups while they remain in its subtree; the test does not migrate cgroups or +write outside its root. Cwd/HOME/TMPDIR alone do not restrict filesystem access. +The supervisor never mounts, provisions, raises limits, changes credentials or +network settings, deletes evidence, or signals a broad PID/process-group match. +If the dedicated leaf/volume cannot be supplied, live execution remains blocked; +do not substitute weaker containment or increase limits to make it pass. + +## Actual test inputs and observations + +One native carrier track: JacobiPcgV1, 4×4×4 cells, 1 m cube, zero origin, density +1 kg/m³, two requested steps, dt0.02 s, tight=false, threads1, observe=false; +material/emitter/collision/model/configured/saved-set absent. Existing ordinary +smoke source semantics remain, so this is real numerical work. Pressure uses +unchanged relative1e-9, absolute1e-12, predicted divergence1e-7, actual1e-5, +Courant1 and max2000 iterations/solve. No larger grid, extra restart, comparison, +producer, corpus or scientific experiment is included. + +Preview: exact edge4, one track,16 MiB, at most3 frame slots. Recording: one +fresh `run/recordings` directory,8 MiB database,1 MiB publication,max_records3. +Rollback journal may use roughly another database plus overhead, within the +kernel output volume; these are component limits, not process RSS. No preserved +483 evidence files or three original databases are inputs or targets. + +1. Validate admission and compiled revision before starting one actual Worker. +2. Reuse existing ProgressGate at first acknowledged accepted step. Poll + `try_recv`, render the real sidebar and yield while waiting, <=10s observation; + never block UI on recv/join. Assert progress1/2 and still-live handle. +3. While the gate holds the Worker, send real egui pointer press/release to + **Cancel simulation**. Assert actual shared cancel flag and held handle; + preserve the reached event, then release gate. No direct substitute call to + `app.cancel` in the test. +4. Keep painting while observing `is_finished`, <=10s. Poll/join only after true. + Require no completed result rows/textures, progress1, cancelled status. +5. Open only the new recording: verify integrity,2 publications (constructor + + first accepted step), accepted_generation1 and terminal outcome cancelled. + Record requested_complete=false separately from integrity_verified=true. + +`events.jsonl` is synced at reached stages. A bounded new `result.json` contains +source/binary identity, gate/UI/join and retained-prefix facts. Supervisor success +requires exit0 **and** that exact complete contract result; exit0 with zero tests +or a missing/incomplete result fails. This is genuine worker/egui interaction +when eventually run, still not native rendered Cancel/focus verification. + +## Teardown and evidence + +Child waits on a private GO pipe until membership and receipt are confirmed. +Limits are applied before exec. Parent TERM/INT set a stop flag without throwing +across fork/FD ownership changes; controlled cleanup closes GO, attempts TERM, +then cgroup.kill/SIGKILL only for the admitted subtree or its exact unreaped +owned child. pidfds avoid recycled-PID signaling. All normal/exception paths +require both `cgroup.events populated=0` and direct-child reaping before success. +Normal SIGCHLD ownership is required before fork. Read/reap/TERM errors are +recorded conservatively and do not skip KILL attempts; ECHILD suppresses any +numeric-PID fallback after ownership is lost. + +Observe cleanup for2s after KILL. If retirement/emptiness cannot be established, +mark accepted=false and teardown unresolved, retain the owned cgroup/volume and +escalate to its authorized owner; never broaden killing or claim success. +Existing absolute TERM/KILL/observation deadlines are reused during exception +cleanup; no second grace or post-KILL wait is added. Catchable supervisor +interrupts enter cleanup. Uncatchable supervisor SIGKILL, +host failure, lost cgroup authority or uninterruptible kernel tasks are outside +a userspace teardown guarantee; resource containment remains dependent on kernel +and reserved-resource custody. Do not silently turn these into a successful +hard-wall or no-leftover claim. + +A preallocated64 KiB supervisor receipt preserves final errors even after child +ENOSPC; finalization attempts remaining descriptor release and restores handlers +even on receipt failure. Logs, partial recordings, stage events and all input +seals remain; no retry or cleanup. Output tmpfs is volatile: its owner must retain +it/copy authorized evidence before unmount/reboot; no automatic transfer occurs. + +## Workload-free checks in this turn + +Long-status egui stress covers original long path, a16 KiB unbroken token and +multiline Unicode/combining text at both logical sizes, full exact copy/end +navigation/edit refusal, pointer-acquired focus and Shift+Tab exit. These remain +headless tests and do not close native accessibility/usability acceptance. +Supervisor unit checks cover cap refusal, ordinary filesystem rejection, actual +immutable memfd refusal, missing/incomplete result, owned non-numerical waiting +process TERM→KILL/reap, injected observation/reap failures and pre-GO receipt +failure cleanup/handler restoration. Mocked cgroups are explicitly not kernel +containment integration evidence. No genuine ignored case is run here. diff --git a/docs/plans/ui-refinement/reports/m1-cancellation-review.md b/docs/plans/ui-refinement/reports/m1-cancellation-review.md new file mode 100644 index 00000000..eabbf0a5 --- /dev/null +++ b/docs/plans/ui-refinement/reports/m1-cancellation-review.md @@ -0,0 +1,104 @@ +# Independent M1 cancellation-contract review + +Reviewed 2026-10-20 on `implementation/ui-m1-cancel-contract-20261020`, +base `e1097581d751c061eaf8c2df45d83de62911a74d`. Scope: the external supervisor, +its unit tests, ignored genuine-worker contract, expanded inert status test, +and the corresponding contract/plan/ledger changes. Only this report was authored +by the reviewer; application production source was not changed. + +## Disposition + +No remaining material source finding in the reviewed diff. Suitable to retain +as a **conditional, unexecuted test contract**. This is not execution admission, +demonstrated genuine cancellation, kernel-containment integration evidence, or +a guarantee against uncontrolled work under arbitrary host failures. + +The contract depends on a trusted pinned test, Linux kernel facilities and +externally reserved exclusive custody of the preconfigured cgroup leaf and +dedicated bounded tmpfs throughout execution. Empty-at-preflight is not a lease. +Unavailable prerequisites must block execution without fallback or provisioning. +The runtime environment marker is not a credential or substitute for separate +execution authority. + +## Source findings resolved + +- `tools/test_supervise_ui_cancel.py`: the owned-process fixture now retains its + pidfd through final signaling/reaping. It no longer signals a numeric PID after + successful reaping, when that PID could have been recycled. +- `supervise_ui_cancel.py::teardown`: normal SIGCHLD semantics are required before + fork; reaping establishes ownership before numeric-PID fallback, and ECHILD + suppresses that fallback. Read/reap/TERM errors remain conservative, preserve + unresolved status and do not skip scoped KILL attempts. No broad process-group + or PID-pattern termination is introduced. +- `planned_limits` retains stricter inherited soft/hard limits rather than + raising them, and the receipt records effective values. Existing absolute + TERM/KILL/wall deadlines are reused by exceptional cleanup; no second grace + period or post-KILL observation window is silently started. +- `write_receipt` completes partial writes and checks forward progress before + fsync. Descriptor ownership/finalization is explicit, signal handlers set a + stop flag, and nested finalization releases remaining descriptors/restores + handlers even when evidence writing fails. Existing close errors block GO and + final acceptance; later cleanup failures can revoke acceptance. + +The immutable memfd copy binds executed bytes to the declared SHA256/size; +growth while copying is rejected. Exact script/source/tree/lock checks precede +GO, and the child cannot exec the test before its leaf membership and admission +receipt are confirmed. Exit zero alone cannot pass: a matching complete result +is required. The source revision remains a caller-supplied build declaration, +explicitly distinct from hermetic build attestation. + +## Numerical and evidence boundaries + +`setup_ui_tests::bounded_genuine_cancel_contract` is ignored and excluded from +the `ui_setup_` filter. It validates admission and compiled identity before +`Worker::start`. Its request is one Jacobi carrier track, 4³ cells, two steps, +one thread, 0.02 s dt, edge-4 preview/16 MiB history and bounded fresh recording. +No model, configured/saved execution, corpus or scientific validation is added. + +If separately admitted, it would hold the existing first-accepted-step gate, +click the real egui Cancel control, release the gate, observe completion while +painting, then poll/join only after `is_finished`. Integrity verification, +two retained publications, accepted generation one and cancelled terminal status +keep recording integrity distinct from requested-run completeness. These are +prospective worker/egui claims, not native rendered interaction evidence. + +The documentation correctly separates one-CPU-equivalent bandwidth from the +monitored aggregate CPU trip and inherited per-process limits; supervisor/memfd +input memory from the child's memory cap; allocated tmpfs capacity from sparse +logical lengths/cumulative writes; and retained log limits from pipe-accepted +bytes. Catchable interrupts enter cleanup. Scheduling, uninterruptible kernel +tasks, supervisor SIGKILL/host failure and lost authority prevent an unconditional +hard-wall or no-leftover guarantee. Unresolved teardown is failure requiring +preserved resources/evidence and escalation to their authorized owner. + +## Verification and remaining gaps + +The reviewer independently ran all **13 supervisor unit tests**, normally and +with Python `-O`, against the final source: both exited zero. They exercised +actual immutable memfd/pipe behavior and an explicitly owned non-numerical +waiting child, plus fault-injected cleanup, identity, limits and receipt checks. +Mock cgroups and volume admission are not a real cgroup/tmpfs integration pass. +Fixture evidence remains under `/tmp/rheon-ui-supervisor-tests-*`; no cleanup +was performed. + +The lead reports nine focused inert UI tests and Rust check/strict Clippy/fmt +passing. The reviewed status test uses fresh egui contexts for long-path, +16 KiB unbroken and multiline Unicode/combining text at both logical sizes; +pointer focus, exact full copy, end navigation, edit refusal and Shift+Tab exit +remain headless evidence. No numerical, model, solver, evaluator, corpus or +native workload was executed by this reviewer. The genuine case is compiled +but unexecuted. Native active-state/focus/accessibility checks, genuine +cancellation, word-count/usability measurements and whole M1 acceptance remain +open. M2–M4 are not admitted by this checkpoint. + +## Reviewed content identity + +| File | SHA256 | +| --- | --- | +| `tools/supervise_ui_cancel.py` | `23d4f18a97076d4908c11fedcd5c378568c9d8dd8bf5211b0116f5ebadc9b201` | +| `tools/test_supervise_ui_cancel.py` | `3d853281ee13e25a95375d73e4b7fc16652da93b6c27862c536fc09659234a98` | +| `src/bin/desktop/setup_ui_tests.rs` | `c1378336b2259a3f37fbe91f68872e8f9f4a2171555d32e752935b3dc819a018` | +| `reports/m1-cancellation-contract.md` | `28cc495dcf5c332bf9c90fe1c6f783509988304aac5e3cadb4453089fb4665b8` | + +A later exact-commit no-run build and external seal can bind a concrete test +binary; this review neither performs that build nor permits executing it. diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index 1ccae772..cb63d636 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -163,122 +163,180 @@ fn ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode() #[test] fn ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector() { for size in [egui::vec2(900.0, 650.0), egui::vec2(1250.0, 820.0)] { - let ctx = context(); - let status = format!( - "Recording unavailable: {}\nPrevious data retained; select a writable directory.", - "long-path/".repeat(300) - ); - let mut app = App::default(); - app.status = status.clone(); - for _ in 0..3 { - let _ = frame(&ctx, size, vec![], |ctx| { + for status in [ + format!( + "Recording unavailable: {}\nPrevious data retained; select a writable directory.", + "long-path/".repeat(300) + ), + format!( + "Blocking error: {}\nAccepted generation 1; acknowledged prefix 2; recording incomplete.", + "x".repeat(16_384) + ), + format!( + "Integrity verified; requested recording incomplete.\n{}\nDo not infer completion from integrity.", + "Δt 0.02 s · μ Pa·s · 読み取り · e\u{301}\n".repeat(256) + ), + ] { + let ctx = context(); + let mut app = App::default(); + app.status = status.clone(); + for _ in 0..3 { + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + } + let output = frame(&ctx, size, vec![], |ctx| { app.sidebar(ctx); }); - } - let output = frame(&ctx, size, vec![], |ctx| { - app.sidebar(ctx); - }); - click(&ctx, size, &output, "Status details", |ctx| { - app.sidebar(ctx); - }); - let output = frame(&ctx, size, vec![], |ctx| { - app.sidebar(ctx); - }); - let launch = rect(&output, "Simulate"); - assert!(launch.bottom() <= size.y && launch.top() > 0.0); - assert!(rect(&output, "Status details").bottom() < size.y); - assert!(output.shapes.iter().any(|shape| matches!(&shape.shape, + click(&ctx, size, &output, "Status details", |ctx| { + app.sidebar(ctx); + }); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + let launch = rect(&output, "Simulate"); + assert!(launch.bottom() <= size.y && launch.top() > 0.0); + assert!(rect(&output, "Status details").bottom() < size.y); + assert!(output.shapes.iter().any(|shape| matches!(&shape.shape, egui::Shape::Text(text) if text.galley.text() == status && shape.clip_rect.height() <= 100.0))); - let form_point = rect(&output, "Rheon").center() + egui::vec2(0.0, 30.0); - let _ = frame( - &ctx, - size, - vec![ - egui::Event::PointerMoved(form_point), - egui::Event::MouseWheel { - unit: egui::MouseWheelUnit::Point, - delta: egui::vec2(0.0, -2000.0), - modifiers: Default::default(), + let form_point = rect(&output, "Rheon").center() + egui::vec2(0.0, 30.0); + let _ = frame( + &ctx, + size, + vec![ + egui::Event::PointerMoved(form_point), + egui::Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: egui::vec2(0.0, -2000.0), + modifiers: Default::default(), + }, + ], + |ctx| { + app.sidebar(ctx); }, - ], - |ctx| { - app.sidebar(ctx); - }, - ); - let output = frame(&ctx, size, vec![], |ctx| { - app.sidebar(ctx); - }); - assert_eq!(launch, rect(&output, "Simulate")); - assert!( - !has(&output, "Rheon"), - "setup form should scroll independently" - ); - click(&ctx, size, &output, "Inspector", |ctx| { - app.sidebar(ctx); - }); - let output = frame(&ctx, size, vec![], |ctx| { - app.sidebar(ctx); - }); - assert!(has(&output, "Numerical inspector")); - assert_eq!(launch, rect(&output, "Simulate")); - let text_id = egui::Id::new("run-status-text"); - ctx.memory_mut(|memory| memory.request_focus(text_id)); - let modifiers = egui::Modifiers { - ctrl: true, - command: true, - ..Default::default() - }; - let _ = frame( - &ctx, - size, - vec![egui::Event::Key { - key: egui::Key::End, - physical_key: None, - pressed: true, - repeat: false, - modifiers, - }], - |ctx| { + ); + let output = frame(&ctx, size, vec![], |ctx| { app.sidebar(ctx); - }, - ); - let state = egui::text_edit::TextEditState::load(&ctx, text_id).unwrap(); - assert_eq!( - state.cursor.char_range().unwrap().primary.index, - status.chars().count() - ); - let _ = frame( - &ctx, - size, - vec![egui::Event::Key { - key: egui::Key::A, - physical_key: None, - pressed: true, - repeat: false, - modifiers, - }], - |ctx| { + }); + assert_eq!(launch, rect(&output, "Simulate")); + assert!( + !has(&output, "Rheon"), + "setup form should scroll independently" + ); + click(&ctx, size, &output, "Inspector", |ctx| { app.sidebar(ctx); - }, - ); - let output = frame( - &ctx, - size, - vec![egui::Event::Copy, egui::Event::Text("unwanted edit".into())], - |ctx| { + }); + let output = frame(&ctx, size, vec![], |ctx| { app.sidebar(ctx); - }, - ); - assert!( - output - .platform_output - .commands - .iter() - .any(|command| matches!(command, + }); + assert!(has(&output, "Numerical inspector")); + assert_eq!(launch, rect(&output, "Simulate")); + let text_id = egui::Id::new("run-status-text"); + let status_point = ctx + .read_response(text_id) + .expect("visible full-status control") + .interact_rect + .center(); + for pressed in [true, false] { + let _ = frame( + &ctx, + size, + vec![ + egui::Event::PointerMoved(status_point), + egui::Event::PointerButton { + pos: status_point, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + |ctx| { + app.sidebar(ctx); + }, + ); + } + assert!(ctx.memory(|memory| memory.has_focus(text_id))); + let modifiers = egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }; + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key: egui::Key::End, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }], + |ctx| { + app.sidebar(ctx); + }, + ); + let state = egui::text_edit::TextEditState::load(&ctx, text_id).unwrap(); + assert_eq!( + state.cursor.char_range().unwrap().primary.index, + status.chars().count() + ); + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key: egui::Key::A, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }], + |ctx| { + app.sidebar(ctx); + }, + ); + let output = frame( + &ctx, + size, + vec![egui::Event::Copy, egui::Event::Text("unwanted edit".into())], + |ctx| { + app.sidebar(ctx); + }, + ); + assert!( + output + .platform_output + .commands + .iter() + .any(|command| matches!(command, egui::OutputCommand::CopyText(text) if text == &status)) - ); - assert_eq!(app.status, status); - assert!(app.worker.is_none()); + ); + assert_eq!(app.status, status); + assert!(app.worker.is_none()); + let _ = frame( + &ctx, + size, + vec![egui::Event::Key { + key: egui::Key::Tab, + physical_key: None, + pressed: true, + repeat: false, + modifiers: egui::Modifiers { + shift: true, + ..Default::default() + }, + }], + |ctx| { + app.sidebar(ctx); + }, + ); + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!( + !ctx.memory(|memory| memory.has_focus(text_id)), + "status must permit keyboard exit" + ); + } } } @@ -645,3 +703,168 @@ fn ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_sign ); } } + +#[test] +#[ignore = "Numerical workload: separate admission and sealed external supervisor required"] +fn bounded_genuine_cancel_contract() { + // This case is deliberately excluded from ui_setup_ and every default test run. + assert_eq!( + std::env::var("RHEON_UI_CANCEL_ADMISSION").as_deref(), + Ok("genuine-cancel-v1") + ); + let root = std::path::PathBuf::from( + std::env::var("RHEON_UI_CANCEL_ROOT").expect("supervised fresh root"), + ); + assert!(root.is_absolute()); + let seal_path = root.join("contract.json"); + assert!(std::fs::metadata(&seal_path).unwrap().len() <= 65_536); + let seal: serde_json::Value = + serde_json::from_slice(&std::fs::read(&seal_path).unwrap()).unwrap(); + assert_eq!( + seal["test"], + "setup_ui_tests::bounded_genuine_cancel_contract" + ); + assert_eq!(seal["commit"], build_identity::source_revision()); + assert_ne!( + build_identity::source_revision(), + "build revision unspecified" + ); + // Supervisor executes immutable memfd bytes, so current_exe is a memfd, not + // the original build pathname. The supervisor binds its SHA256/size to seal. + assert_eq!( + std::fs::metadata("/proc/self/exe").unwrap().len(), + seal["binary_bytes"].as_u64().unwrap() + ); + let recording_root = root.join("recordings"); + assert_eq!(std::fs::read_dir(&recording_root).unwrap().count(), 0); + let limits = rheon::recording::RecordingLimits { + max_database_bytes: 8 * 1024 * 1024, + max_record_bytes: 1024 * 1024, + max_records: 3, + }; + // Preserve each reached stage before any assertion can fail. No cleanup runs. + let event = |stage: &str| { + use std::io::Write; + let mut log = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(root.join("events.jsonl")) + .unwrap(); + writeln!(log, "{}", serde_json::json!({"stage": stage})).unwrap(); + log.sync_all().unwrap(); + }; + event("admission_checked_before_worker_start"); + let (reached, gate) = std::sync::mpsc::sync_channel(1); + let (release, resume) = std::sync::mpsc::sync_channel(1); + let request = Request { + collision_mesh: None, + size: 4, + steps: 2, + tight: false, + threads: 1, + methods: vec![PressureImplementation::JacobiPcgV1], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Edge(4), + domain_side: 1.0, + requested_dt: 0.02, + material: None, + material_color: [85, 190, 245], + recording_limits: limits, + recording_directory: recording_root.to_str().unwrap().into(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: Some(Arc::new(ProgressGate { + reached, + release: std::sync::Mutex::new(resume), + })), + failure_gate: None, + }; + let mut app = App::default(); + app.running_request = Some(request.clone()); + let worker = Worker::start(request).unwrap(); + app.history = Some(Arc::clone(&worker.history)); + let progress = Arc::clone(&worker.progress); + let cancel = Arc::clone(&worker.cancel); + app.worker = Some(worker); + let ctx = context(); + let size = egui::vec2(900.0, 650.0); + let deadline = Instant::now() + Duration::from_secs(10); + loop { + match gate.try_recv() { + Ok(()) => break, + Err(std::sync::mpsc::TryRecvError::Empty) => {} + Err(error) => panic!("gate disconnected: {error}"), + } + assert!(Instant::now() < deadline, "first-step observation timeout"); + assert!( + !app.worker.as_ref().unwrap().handle.is_finished(), + "worker ended before gate" + ); + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + std::thread::yield_now(); + } + event("first_accepted_step_gate_held"); + assert_eq!(progress.load(Ordering::Relaxed), 1); + assert!(!app.worker.as_ref().unwrap().handle.is_finished()); + let output = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + assert!(has(&output, "1/2 steps")); + click(&ctx, size, &output, "Cancel simulation", |ctx| { + app.sidebar(ctx); + }); + assert!(cancel.load(Ordering::Relaxed)); + assert!( + !app.worker.as_ref().unwrap().handle.is_finished(), + "gate must still hold worker" + ); + event("actual_egui_cancel_clicked_while_gate_held"); + release.send(()).unwrap(); + let deadline = Instant::now() + Duration::from_secs(10); + while !app.worker.as_ref().unwrap().handle.is_finished() { + assert!(Instant::now() < deadline, "retirement observation timeout"); + let _ = frame(&ctx, size, vec![], |ctx| { + app.sidebar(ctx); + }); + std::thread::yield_now(); + } + // Join only after is_finished; neither observation loop blocks on recv/join. + app.poll(&ctx); + event("actual_worker_joined"); + assert!(app.worker.is_none() && app.rows.is_empty() && app.textures.is_empty()); + assert_eq!(progress.load(Ordering::Relaxed), 1); + assert!(app.status.contains("Cancelled") || app.status.contains("cancelled")); + let path = app.history.as_ref().unwrap().carrier_inspectors[0] + .archive_path() + .unwrap(); + assert!(path.starts_with(&recording_root)); + let reader = rheon::recording::RecordingReader::open(&path, limits).unwrap(); + reader.verify().unwrap(); + assert_eq!(reader.count().unwrap(), 2); + assert_eq!(reader.status().unwrap().outcome, "cancelled"); + assert_eq!(reader.status().unwrap().accepted_generation, Some(1)); + let result = serde_json::json!({ + "contract": "genuine-cancel-v1", "test": "setup_ui_tests::bounded_genuine_cancel_contract", + "source": build_identity::source_revision(), "binary_sha256": seal["binary_sha256"], + "gate_held": true, "ui_cancel": true, "joined": true, "progress": 1, + "integrity_verified": true, "cancelled": true, "record_count": 2, + "accepted_generation": 1, "requested_complete": false, "archive": path, + "ui_status": app.status, + }); + use std::io::Write; + let mut file = std::fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(root.join("result.json")) + .unwrap(); + file.write_all(serde_json::to_string_pretty(&result).unwrap().as_bytes()) + .unwrap(); + file.sync_all().unwrap(); + event("complete_contract_result_preserved"); +} diff --git a/tools/supervise_ui_cancel.py b/tools/supervise_ui_cancel.py new file mode 100644 index 00000000..a6220d89 --- /dev/null +++ b/tools/supervise_ui_cancel.py @@ -0,0 +1,484 @@ +#!/usr/bin/env python3 +"""Admit one sealed, ignored numerical cancellation test; never build or provision. + +Linux only. Requires separate live authority, an exclusive preconfigured cgroup +and an empty dedicated <=32MiB tmpfs. No fallback, mount, cleanup or limit changes. +This contains a trusted exact test, not hostile code with cgroup/filesystem access. +""" +import argparse +import ctypes +import fcntl +import hashlib +import json +import os +from pathlib import Path +import resource +import selectors +import signal +import stat +import subprocess +import sys +import time + +# Linux UAPI ; some Python builds omit these named constants. +F_ADD_SEALS, F_GET_SEALS, IMMUTABLE_SEALS = 1033, 1034, 0x000F +F_SETPIPE_SZ, F_GETPIPE_SZ, PIPE_BYTES = 1031, 1032, 4096 +TEST = "setup_ui_tests::bounded_genuine_cancel_contract" +MIB = 1024 * 1024 +WALL = 25.0 # KILL disposition deadline, not guaranteed D-state exit. +GRACE = 1.0 +LOG_LIMIT = MIB # Each of stdout/stderr; combined <=2MiB retained. +LIMITS = {"cpu_soft_s": 5, "cpu_hard_s": 6, "address_space_bytes": 1024 * MIB, + "file_bytes": 8 * MIB, "open_files": 64, "output_volume_bytes": 32 * MIB, + "memory_bytes": 512 * MIB, "pids": 16, "wall_s": WALL, "term_grace_s": GRACE} + + +class AdmissionError(ValueError): + pass + + +def require(ok, message): + if not ok: + raise AdmissionError(message) + + +def encoded(value): + return (json.dumps(value, sort_keys=True, indent=2) + "\n").encode() + + +def git(repo, *args): + env = {"PATH": "/usr/bin:/bin", "LANG": "C", "GIT_CONFIG_NOSYSTEM": "1", + "GIT_CONFIG_GLOBAL": os.devnull, "GIT_NO_REPLACE_OBJECTS": "1", + "GIT_OPTIONAL_LOCKS": "0"} + return subprocess.check_output(["git", "-c", "core.fsmonitor=false", "-C", str(repo), + *args], env=env, timeout=10).decode().strip() + + +def mount_for(path): + # Mountinfo escapes are deliberately refused: no guessed decoding/alias path. + matches = [] + for line in Path("/proc/self/mountinfo").read_text().splitlines(): + before, after = line.split(" - ", 1) + fields = before.split() + mount = fields[4] + if "\\" in mount: + continue + if path == Path(mount) or Path(mount) in path.parents: + matches.append((len(mount), Path(mount), after.split()[0], after.split()[2])) + require(matches, "no actual mount identity") + return max(matches, key=lambda item: item[0])[1:] + + +def check_cgroup_values(values): + require(values["cpu.max"].split() == ["10000", "10000"], "one-CPU cgroup quota required") + require(values["cpu.max.burst"].strip() == "0", "CPU burst must be zero") + for key, cap in [("memory.max", 512 * MIB), ("pids.max", 16)]: + require(values[key].strip().isdigit() and 0 < int(values[key]) <= cap, key + " cap") + require(values["memory.swap.max"].strip() == "0", "swap must be disabled in leaf") + + +class Cgroup: + def __init__(self, path): + self.path = path.resolve(strict=True) + mount, kind, _ = mount_for(self.path) + require(kind == "cgroup2" and self.path != mount, "dedicated cgroup-v2 leaf required") + require(not any(p.is_dir() for p in self.path.iterdir()), "leaf has child cgroups") + keys = ["cpu.max", "cpu.max.burst", "memory.max", "memory.swap.max", "pids.max"] + self.values = {key: (self.path / key).read_text().strip() for key in keys} + check_cgroup_values(self.values) + require(not self.populated() and not self.members(), "cgroup must be exclusive and empty") + require(all(os.access(self.path / key, os.W_OK) for key in ["cgroup.procs", "cgroup.kill"]), + "leaf admission/kill rights unavailable") + self.initial_cpu = self.cpu_usec() + + def members(self): + return [int(p) for p in (self.path / "cgroup.procs").read_text().split()] + + def populated(self): + return dict(line.split() for line in (self.path / "cgroup.events").read_text().splitlines())["populated"] != "0" + + def cpu_usec(self): + return int(dict(line.split() for line in (self.path / "cpu.stat").read_text().splitlines())["usage_usec"]) + + def admit(self, pid): + (self.path / "cgroup.procs").write_text(str(pid)) + require(self.members() == [pid], "exclusive admitted leaf must contain only the owned child") + + def term(self): + # pidfds avoid signaling a recycled PID outside this exclusive subtree. + for pid in self.members(): + try: + fd = os.pidfd_open(pid) + try: + if pid in self.members(): + signal.pidfd_send_signal(fd, signal.SIGTERM) + finally: + os.close(fd) + except ProcessLookupError: + pass + + def kill(self): + (self.path / "cgroup.kill").write_text("1") + + +def output_volume(path): + path = path.resolve(strict=True) + mount, kind, options = mount_for(path) + require(path == mount and kind == "tmpfs", "dedicated tmpfs mount root required") + require("noswap" in options.split(","), "dedicated output tmpfs must use noswap") + require(not any(path.iterdir()), "output mount must be empty; never reuse/delete evidence") + fs = os.statvfs(path) + require(0 < fs.f_blocks * fs.f_frsize <= 32 * MIB, "hard <=32MiB output filesystem required") + require(0 < fs.f_files <= 1024, "hard <=1024 output inodes required") + return path + + +def seal_inputs(repo, seal_path): + require(seal_path.stat().st_size <= 65536, "seal too large") + seal = json.loads(seal_path.read_text()) + require(seal["test"] == TEST and seal["toolchain"] == "1.92.0", "wrong test/toolchain seal") + require(hashlib.sha256(Path(__file__).read_bytes()).hexdigest() == seal["supervisor_sha256"], + "supervisor source seal mismatch") + require(not git(repo, "status", "--porcelain", "--untracked-files=all"), "source must be clean") + require(git(repo, "rev-parse", "HEAD") == seal["commit"], "commit seal mismatch") + require(git(repo, "rev-parse", "HEAD^{tree}") == seal["tree"], "tree seal mismatch") + require(hashlib.sha256((repo / "Cargo.lock").read_bytes()).hexdigest() == seal["cargo_lock_sha256"], + "lock seal mismatch") + binary = Path(seal["binary"]).resolve(strict=True) + source_fd = os.open(binary, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC) + fd = None + try: + fd = os.memfd_create("rheon-ui-cancel-sealed", os.MFD_ALLOW_SEALING | os.MFD_CLOEXEC) + info = os.fstat(source_fd) + require(stat.S_ISREG(info.st_mode) and 0 < info.st_size <= 512 * MIB, "512MiB binary input limit") + digest = hashlib.sha256() + copied = 0 + while block := os.read(source_fd, MIB): + copied += len(block) + require(copied <= info.st_size, "binary grew while sealing") + digest.update(block) + view = memoryview(block) + while view: + view = view[os.write(fd, view):] + require(digest.hexdigest() == seal["binary_sha256"], "binary hash mismatch") + require(os.fstat(fd).st_size == seal["binary_bytes"] == info.st_size, "binary size seal") + fcntl.fcntl(fd, F_ADD_SEALS, IMMUTABLE_SEALS) + require(fcntl.fcntl(fd, F_GET_SEALS) & IMMUTABLE_SEALS == IMMUTABLE_SEALS, + "immutable executable seals unavailable") + os.lseek(fd, 0, os.SEEK_SET) + require(os.read(fd, 4) == b"\x7fELF", "compiled ELF test binary required") + os.lseek(fd, 0, os.SEEK_SET) + return seal, fd + except BaseException: + if fd is not None: + os.close(fd) + raise + finally: + os.close(source_fd) + + +def planned_limits(): + values = [] + for key, cap in [(resource.RLIMIT_CPU, (5, 6)), + (resource.RLIMIT_AS, (1024 * MIB, 1024 * MIB)), + (resource.RLIMIT_FSIZE, (8 * MIB, 8 * MIB)), + (resource.RLIMIT_NOFILE, (64, 64)), (resource.RLIMIT_CORE, (0, 0))]: + inherited = resource.getrlimit(key) + effective = tuple(bound if old == resource.RLIM_INFINITY else min(old, bound) + for old, bound in zip(inherited, cap)) + values.append((key, effective)) + return values + + +def child_limits(): + for key, value in planned_limits(): + resource.setrlimit(key, value) # Never raise a stricter inherited limit. + + +def write_receipt(fd, value): + raw = encoded(value) + require(len(raw) <= 65536, "receipt size") + view = memoryview(raw + b" " * (65536 - len(raw))) + offset = 0 + while view: + written = os.pwrite(fd, view, offset) + require(written > 0, "receipt write made no progress") + offset += written + view = view[written:] + os.fsync(fd) + + +def result_complete(root, seal): + path = root / "result.json" + require(path.is_file() and not path.is_symlink() and path.stat().st_size <= 65536, "missing bounded result") + value = json.loads(path.read_text()) + require(value.get("contract") == "genuine-cancel-v1" and value.get("source") == seal["commit"] + and value.get("test") == TEST and value.get("binary_sha256") == seal["binary_sha256"], "result identity") + for field in ["gate_held", "ui_cancel", "joined", "integrity_verified", "cancelled"]: + require(value.get(field) is True, "incomplete result: " + field) + require(value.get("progress") == 1 and value.get("record_count") == 2 + and value.get("accepted_generation") == 1 and value.get("requested_complete") is False, + "retained-prefix/completeness result") + return value + + +def teardown(group, pid, pidfd, status, term_at, kill_at, wall_deadline=None): + """Bounded observation/kill disposition; never broaden beyond owned resources.""" + errors = [] + ownership_known = True + def attempt(operation): + try: + return operation() + except (OSError, ValueError, KeyError) as error: + if len(errors) < 16: + errors.append(str(error)) + return None + def populated(): + value = attempt(group.populated) + return True if value is None else value + def reap(): + nonlocal status, ownership_known + if status is None: + try: + value = os.waitpid(pid, os.WNOHANG) + if value[0]: + status = value[1] + except ChildProcessError as error: + ownership_known = False + if len(errors) < 16: + errors.append(str(error)) + except OSError as error: + if len(errors) < 16: + errors.append(str(error)) + def signal_child(sig): + if status is None: + if pidfd is not None: + signal.pidfd_send_signal(pidfd, sig) + elif ownership_known: + os.kill(pid, sig) # Suppressed after ECHILD/ownership loss. + reap() # Establish ownership before any numeric-PID fallback. + if status is None or populated(): + if term_at is None: + term_at = time.monotonic() + attempt(group.term) + attempt(lambda: signal_child(signal.SIGTERM)) + grace = min(term_at + GRACE, wall_deadline if wall_deadline is not None else float("inf")) + if kill_at is not None: + grace = kill_at + while time.monotonic() < grace: + reap() + if status is not None and not populated(): + break + time.sleep(0.01) + if status is None or populated(): + if kill_at is None: + kill_at = time.monotonic() + attempt(group.kill) + attempt(lambda: signal_child(signal.SIGKILL)) + deadline = (kill_at if kill_at is not None else time.monotonic()) + 2.0 + while time.monotonic() < deadline: + reap() + if status is not None and not populated(): + break + time.sleep(0.01) + return dict(child_reaped=status is not None, child_status=status, + cgroup_empty=not populated(), teardown_errors=errors) + + +def supervise(repo, seal_path, volume, group_path): + require(signal.getsignal(signal.SIGCHLD) == signal.SIG_DFL, + "normal SIGCHLD direct-child ownership required") + require(sys.platform == "linux" and hasattr(os, "pidfd_open") + and hasattr(signal, "pidfd_send_signal"), "Linux pidfd support required") + fds, log_fds = set(), [] + state = {"limits": LIMITS, "stage": "preflight", "reason": None, "accepted": False} + group = selector = pid = pidfd = child_status = receipt_fd = gate_w = None + term_at = kill_at = None + handlers, interrupted = {}, [] + def own(fd): + fds.add(fd) + return fd + def close(fd): + if fd in fds: + fds.remove(fd) + try: + os.close(fd) + except OSError as error: + state["accepted"] = False + state.setdefault("close_errors", []).append(str(error)) + def pipe(): + pair = os.pipe2(os.O_CLOEXEC) + return tuple(own(fd) for fd in pair) + try: + group = Cgroup(group_path) # Never provision/change any limits. + volume = output_volume(volume) # No mount/quota fallback. + seal, binary_fd = seal_inputs(repo, seal_path) + own(binary_fd) + root = volume / "run" + root.mkdir(mode=0o700) + (root / "recordings").mkdir() + (root / "tmp").mkdir() + (root / "contract.json").write_bytes(encoded(dict(seal, limits=LIMITS))) + receipt_fd = own(os.open(root / "supervisor.json", os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o600)) + os.posix_fallocate(receipt_fd, 0, 65536) # Reserve evidence even after ENOSPC. + state.update(seal=seal, cgroup=str(group.path), cgroup_values=group.values, + effective_rlimits=planned_limits(), stage="prepared") + write_receipt(receipt_fd, state) + gate_r, gate_w = pipe() + out_r, out_w = pipe() + err_r, err_w = pipe() + for fd in [out_w, err_w]: + fcntl.fcntl(fd, F_SETPIPE_SZ, PIPE_BYTES) + require(fcntl.fcntl(fd, F_GETPIPE_SZ) == PIPE_BYTES, "bounded output pipe unavailable") + selector = selectors.DefaultSelector() + def stop(number, _frame): + interrupted.append(number) # Never raise during fork/FD ownership changes. + for sig in [signal.SIGTERM, signal.SIGINT]: + handlers[sig] = signal.signal(sig, stop) + started = time.monotonic() + pid = os.fork() + if pid == 0: + try: + os.setsid() + libc = ctypes.CDLL(None, use_errno=True) + if libc.prctl(1, signal.SIGKILL, 0, 0, 0) != 0: + os._exit(125) + child_limits() + null_fd = os.open(os.devnull, os.O_RDONLY | os.O_CLOEXEC) + os.dup2(null_fd, 0) + os.dup2(out_w, 1) + os.dup2(err_w, 2) + # Parent's write end must not keep this own GO pipe alive. + os.close(gate_w) + if os.read(gate_r, 1) != b"G": + os._exit(125) + os.chdir(root) + env = {"LANG": "C.UTF-8", "HOME": str(root), "TMPDIR": str(root / "tmp"), + "RHEON_UI_CANCEL_ROOT": str(root), "RHEON_UI_CANCEL_ADMISSION": "genuine-cancel-v1"} + os.execve(binary_fd, [seal["binary"], "--exact", TEST, "--ignored", "--nocapture", "--test-threads=1"], env) + except BaseException: + os._exit(125) + pidfd = own(os.pidfd_open(pid)) + close(gate_r) + close(out_w) + close(err_w) + group.admit(pid) # Test cannot exec before membership confirmed. + state["output_observed_bytes"] = {} + for stream, name in [(out_r, "stdout.log"), (err_r, "stderr.log")]: + os.set_blocking(stream, False) + log_fd = own(os.open(root / name, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)) + log_fds.append(log_fd) + selector.register(stream, selectors.EVENT_READ, [log_fd, 0, name]) + state["output_observed_bytes"][name] = 0 + require(not interrupted, "supervisor interrupted before GO") + require(not state.get("close_errors"), "control descriptor close failed before GO") + state["stage"] = "admitted" + write_receipt(receipt_fd, state) + os.write(gate_w, b"G") + close(gate_w) + gate_w = None + while True: + require(not interrupted, "supervisor interrupted: " + str(interrupted)) + now = time.monotonic() + if child_status is None: + reaped, value = os.waitpid(pid, os.WNOHANG) + if reaped: + child_status = value + if child_status is not None and not group.populated() and not selector.get_map(): + break + cpu = group.cpu_usec() - group.initial_cpu + trigger = None + if now - started >= WALL - GRACE: + trigger = "wall deadline" + elif cpu >= 5_000_000: + trigger = "aggregate CPU observation trip" + elif child_status is not None and group.populated(): + trigger = "direct child exited; terminate remaining descendants" + if trigger and term_at is None: + state["reason"] = trigger + term_at = now + group.term() + if term_at is not None and kill_at is None and (now - term_at >= GRACE or now - started >= WALL): + group.kill() + kill_at = now + if kill_at is not None and now - kill_at >= 2.0: + raise AdmissionError("containment unresolved after KILL") + for key, _ in selector.select(0.02): + data = os.read(key.fd, min(65536, max(1, LOG_LIMIT - key.data[1] + 1))) + if not data: + selector.unregister(key.fd) + close(key.fd) + continue + log_fd, count, name = key.data + kept = memoryview(data[:max(0, LOG_LIMIT - count)]) + while kept: + kept = kept[os.write(log_fd, kept):] + key.data[1] += len(data) + state["output_observed_bytes"][name] = key.data[1] + if key.data[1] > LOG_LIMIT: + selector.unregister(key.fd) + close(key.fd) # No continued drain/flood during TERM grace. + if term_at is None: + state["reason"] = "captured output limit" + term_at = time.monotonic() + group.term() + state.update(stage="finished", elapsed_s=time.monotonic() - started, + cpu_usec=group.cpu_usec() - group.initial_cpu) + require(child_status == 0 and state["reason"] is None, "bounded test failed/terminated") + state["result"] = result_complete(root, seal) + require(not state.get("close_errors"), "control descriptor close failure") + state["accepted"] = True # Final teardown may still revoke acceptance. + except BaseException as error: + state["reason"] = type(error).__name__ + ": " + str(error)[:4096] + raise + finally: + try: + close(gate_w) + if pid is not None: + final = teardown(group, pid, pidfd, child_status, term_at, kill_at, started + WALL) + state.update(final) + if not final["child_reaped"] or not final["cgroup_empty"] or final["teardown_errors"]: + state["accepted"] = False + state["reason"] = "teardown unresolved; preserve owned leaf/evidence and escalate" + if selector is not None: + selector.close() + for fd in log_fds: + try: + os.fsync(fd) + except OSError as error: + state["accepted"] = False + state.setdefault("log_errors", []).append(str(error)) + for fd in list(fds - {receipt_fd}): + close(fd) + if receipt_fd is not None: + write_receipt(receipt_fd, state) + finally: + # Even receipt/close errors cannot skip remaining FD release/handler restoration. + for fd in list(fds): + close(fd) + for sig, handler in handlers.items(): + signal.signal(sig, handler) + return state + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repo", type=Path, required=True) + parser.add_argument("--seal", type=Path, required=True) + parser.add_argument("--output-volume", type=Path, required=True) + parser.add_argument("--cgroup", type=Path, required=True) + parser.add_argument("--admit-genuine-cancellation", action="store_true", required=True, + help="Execution authority must be granted separately; this flag is not approval.") + args = parser.parse_args() + try: + value = supervise(args.repo.resolve(), args.seal.resolve(), args.output_volume, args.cgroup) + require(value["accepted"], "final teardown/evidence check failed") + print(json.dumps(value, sort_keys=True)) + except (AdmissionError, OSError, ValueError, subprocess.SubprocessError) as error: + print("UI cancellation contract refused/failed: " + str(error), file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/test_supervise_ui_cancel.py b/tools/test_supervise_ui_cancel.py new file mode 100644 index 00000000..4f0e7321 --- /dev/null +++ b/tools/test_supervise_ui_cancel.py @@ -0,0 +1,276 @@ +"""No numerical/native work: cap/identity contracts and owned waiting-process teardown. + +Kernel cgroup/tmpfs enforcement is not emulated as an integration pass. All own +fixture files remain under /tmp/rheon-ui-supervisor-tests-*; no cleanup occurs. +""" +import errno +import hashlib +import json +import os +from pathlib import Path +import signal +import tempfile +import unittest +from unittest.mock import patch + +import supervise_ui_cancel as s + + +class ContractTests(unittest.TestCase): + def setUp(self): + self.root = Path(tempfile.mkdtemp(prefix="rheon-ui-supervisor-tests-")) + + def test_caps_reject_unlimited_cpu_memory_swap_and_pids(self): + good = {"cpu.max": "10000 10000", "cpu.max.burst": "0", "memory.max": str(512*s.MIB), + "memory.swap.max": "0", "pids.max": "16"} + s.check_cgroup_values(good) + for key, value in [("cpu.max", "max 10000"), ("cpu.max.burst", "1"), + ("memory.max", "max"), ("memory.swap.max", "1"), ("pids.max", "17")]: + with self.subTest(key=key), self.assertRaises(s.AdmissionError): + s.check_cgroup_values(dict(good, **{key: value})) + + def test_ordinary_files_cannot_impersonate_cgroup_or_bounded_volume(self): + with self.assertRaises(s.AdmissionError): + s.Cgroup(self.root) + with self.assertRaises(s.AdmissionError): + s.output_volume(self.root) + + def test_output_pipe_capacity_has_an_actual_kernel_bound(self): + read_fd, write_fd = os.pipe() + try: + s.fcntl.fcntl(write_fd, s.F_SETPIPE_SZ, s.PIPE_BYTES) + self.assertEqual(s.fcntl.fcntl(write_fd, s.F_GETPIPE_SZ), 4096) + finally: + os.close(read_fd) + os.close(write_fd) + + def test_per_process_limits_are_applied_before_exec(self): + with patch.object(s.resource, "setrlimit") as call, \ + patch.object(s.resource, "getrlimit", return_value=(s.resource.RLIM_INFINITY, s.resource.RLIM_INFINITY)): + s.child_limits() + self.assertEqual(len(call.call_args_list), 5) + call.assert_any_call(s.resource.RLIMIT_CPU, (5, 6)) + call.assert_any_call(s.resource.RLIMIT_AS, (1024*s.MIB, 1024*s.MIB)) + call.assert_any_call(s.resource.RLIMIT_FSIZE, (8*s.MIB, 8*s.MIB)) + call.assert_any_call(s.resource.RLIMIT_CORE, (0, 0)) + + def test_stricter_inherited_limits_are_never_raised(self): + with patch.object(s.resource, "getrlimit", return_value=(1, 2)), \ + patch.object(s.resource, "setrlimit") as call: + s.child_limits() + call.assert_any_call(s.resource.RLIMIT_CPU, (1, 2)) + call.assert_any_call(s.resource.RLIMIT_AS, (1, 2)) + call.assert_any_call(s.resource.RLIMIT_CORE, (0, 0)) + + def test_binary_copy_is_immutable_and_growth_or_memfd_failure_is_refused(self): + repo = self.root / "repo" + repo.mkdir() + (repo / "Cargo.lock").write_bytes(b"locked fixture") + binary = self.root / "binary" + raw = b"\x7fELFexplicit inert bytes; never executed" + binary.write_bytes(raw) + seal = {"test": s.TEST, "toolchain": "1.92.0", "commit": "a"*40, "tree": "b"*40, + "cargo_lock_sha256": hashlib.sha256(b"locked fixture").hexdigest(), + "binary": str(binary), "binary_sha256": hashlib.sha256(raw).hexdigest(), "binary_bytes": len(raw)} + seal["supervisor_sha256"] = hashlib.sha256(Path(s.__file__).read_bytes()).hexdigest() + path = self.root / "seal.json" + path.write_text(json.dumps(seal)) + def git(_repo, *args): + return {("status", "--porcelain", "--untracked-files=all"): "", + ("rev-parse", "HEAD"): "a"*40, ("rev-parse", "HEAD^{tree}"): "b"*40}[args] + with patch.object(s, "git", git): + _, fd = s.seal_inputs(repo, path) + try: + with self.assertRaises(OSError) as raised: + os.write(fd, b"altered") + self.assertEqual(raised.exception.errno, errno.EPERM) + binary.write_bytes(b"changed input pathname after sealing") + self.assertEqual(os.read(fd, len(raw)), raw) + finally: + os.close(fd) + binary.write_bytes(raw) + with patch.object(s.os, "memfd_create", side_effect=OSError("fixture allocation refusal")): + with self.assertRaises(OSError): + s.seal_inputs(repo, path) + with patch.object(s.os, "read", side_effect=[raw, b"growing input", b""]): + with self.assertRaisesRegex(s.AdmissionError, "grew"): + s.seal_inputs(repo, path) + + def test_exit_zero_or_incomplete_receipt_cannot_pass(self): + seal = {"commit": "a"*40, "binary_sha256": "c"*64} + with self.assertRaises(s.AdmissionError): + s.result_complete(self.root, seal) + value = {"contract": "genuine-cancel-v1", "test": s.TEST, "source": "a"*40, + "binary_sha256": "c"*64, "gate_held": True, "ui_cancel": True, "joined": True, + "integrity_verified": True, "cancelled": True, "progress": 1, + "record_count": 2, "accepted_generation": 1, "requested_complete": False} + (self.root / "result.json").write_text(json.dumps(value)) + self.assertEqual(s.result_complete(self.root, seal), value) + for key, bad in [("record_count", 3), ("ui_cancel", False), ("binary_sha256", "d"*64), + ("requested_complete", True), ("test", "some_other_test")]: + (self.root / "result.json").write_text(json.dumps(dict(value, **{key: bad}))) + with self.subTest(key=key), self.assertRaises(s.AdmissionError): + s.result_complete(self.root, seal) + + def test_owned_non_numerical_process_gets_term_then_kill_and_is_reaped(self): + read_fd, write_fd = os.pipe() + pid = os.fork() + if pid == 0: + os.close(read_fd) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + os.write(write_fd, b"R") + os.close(write_fd) + while True: + signal.pause() + os.close(write_fd) + fd = os.pidfd_open(pid) + events = [] + class OwnedProcessFixture: + def populated(self): + return Path(f"/proc/{pid}").exists() + def term(self): + events.append("TERM") + signal.pidfd_send_signal(fd, signal.SIGTERM) + def kill(self): + events.append("KILL") + signal.pidfd_send_signal(fd, signal.SIGKILL) + try: + self.assertEqual(os.read(read_fd, 1), b"R") + result = s.teardown(OwnedProcessFixture(), pid, fd, None, None, None) + self.assertEqual(events, ["TERM", "KILL"]) + self.assertTrue(result["child_reaped"] and result["cgroup_empty"]) + (self.root / "owned-process-result.json").write_text(json.dumps(result)) + finally: + os.close(read_fd) + try: + signal.pidfd_send_signal(fd, signal.SIGKILL) + except ProcessLookupError: + pass + try: + os.waitpid(pid, 0) + except ChildProcessError: + pass + finally: + os.close(fd) + + def test_pre_go_receipt_failure_closes_gate_reaps_child_and_restores_handlers(self): + # Fault-injected admission only: no executable or numerical code is run. + fake_path = self.root / "leaf" + fake_path.mkdir() + instances = [] + class WaitingChildGroup: + values = {"fixture": "NOT kernel containment evidence"} + initial_cpu = 0 + path = fake_path + fd = None + pid = None + def __init__(self, _path): + instances.append(self) + def admit(self, pid): + self.pid = pid + self.fd = os.pidfd_open(pid) + def populated(self): + return self.pid is not None and Path(f"/proc/{self.pid}").exists() + def cpu_usec(self): + return 0 + def term(self): + signal.pidfd_send_signal(self.fd, signal.SIGTERM) + def kill(self): + signal.pidfd_send_signal(self.fd, signal.SIGKILL) + real_write = s.write_receipt + writes = [] + def fail_before_go(fd, value): + writes.append(value["stage"]) + if len(writes) == 2: + raise OSError("injected pre-GO receipt failure") + real_write(fd, value) + binary_fd = os.memfd_create("inert-never-executed", os.MFD_CLOEXEC) + before = {sig: signal.getsignal(sig) for sig in [signal.SIGTERM, signal.SIGINT]} + try: + with patch.object(s, "Cgroup", WaitingChildGroup), \ + patch.object(s, "output_volume", return_value=self.root), \ + patch.object(s, "seal_inputs", return_value=({"binary": "never-executed"}, binary_fd)), \ + patch.object(s, "write_receipt", fail_before_go): + with self.assertRaisesRegex(OSError, "pre-GO"): + s.supervise(self.root, self.root, self.root, fake_path) + value = json.loads((self.root / "run/supervisor.json").read_text()) + self.assertFalse(value["accepted"]) + self.assertTrue(value["child_reaped"] and value["cgroup_empty"]) + self.assertIn("pre-GO", value["reason"]) + self.assertEqual(before, {sig: signal.getsignal(sig) for sig in before}) + finally: + for instance in instances: + if instance.fd is not None: + os.close(instance.fd) + + def test_echild_without_pidfd_never_signals_a_recycled_numeric_pid(self): + events = [] + class LostOwnership: + def populated(self): + return True + def term(self): + events.append("TERM") + def kill(self): + events.append("KILL") + with patch.object(s.os, "waitpid", side_effect=ChildProcessError("ECHILD")), \ + patch.object(s.os, "kill") as numeric_signal, patch.object(s.time, "sleep"), \ + patch.object(s.time, "monotonic", side_effect=range(100)): + result = s.teardown(LostOwnership(), 12345, None, None, 0.0, None) + numeric_signal.assert_not_called() + self.assertEqual(events, ["KILL"]) + self.assertFalse(result["child_reaped"]) + + def test_cleanup_does_not_restart_expired_term_or_kill_deadlines(self): + for kill_at in [None, 25.0]: + events = [] + class ExpiredDeadline: + def populated(self): + return True + def term(self): + events.append("TERM") + def kill(self): + events.append("KILL") + def sleep(_seconds): + self.assertIn("KILL", events, "expired TERM grace must not restart") + self.assertIsNone(kill_at, "expired post-KILL observation must not restart") + with patch.object(s.os, "waitpid", side_effect=ChildProcessError("ECHILD")), \ + patch.object(s.signal, "pidfd_send_signal"), patch.object(s.time, "sleep", sleep), \ + patch.object(s.time, "monotonic", side_effect=range(100, 200)): + result = s.teardown(ExpiredDeadline(), 12345, 9, None, 24.0, kill_at, 25.0) + self.assertEqual(events, ["KILL"]) + self.assertFalse(result["child_reaped"] or result["cgroup_empty"]) + + def test_receipt_short_writes_are_completed_before_fsync(self): + fd = os.open(self.root / "receipt.json", os.O_CREAT | os.O_RDWR, 0o600) + original = os.pwrite + def short_write(target, data, offset): + return original(target, data[:97], offset) + try: + with patch.object(s.os, "pwrite", short_write): + s.write_receipt(fd, {"accepted": False, "reason": "fixture"}) + self.assertEqual(os.fstat(fd).st_size, 65536) + self.assertEqual(json.loads((self.root / "receipt.json").read_text())["reason"], "fixture") + finally: + os.close(fd) + + def test_unreadable_population_and_reap_error_do_not_skip_kill(self): + events = [] + class BrokenObservation: + def populated(self): + raise OSError("injected events read error") + def term(self): + events.append("TERM") + raise OSError("injected TERM refusal") + def kill(self): + events.append("KILL") + with patch.object(s.os, "waitpid", side_effect=ChildProcessError("injected reap error")), \ + patch.object(s.signal, "pidfd_send_signal"), patch.object(s.time, "sleep"), \ + patch.object(s.time, "monotonic", side_effect=range(100)): + result = s.teardown(BrokenObservation(), 12345, 9, None, None, None) + self.assertEqual(events, ["TERM", "KILL"]) + self.assertFalse(result["child_reaped"] or result["cgroup_empty"]) + self.assertTrue(result["teardown_errors"]) + + +if __name__ == "__main__": + unittest.main() From 676d8a2507db4b618eac1fc7d26e5021d46cac39 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 22:49:15 +0000 Subject: [PATCH 06/25] refactor(desktop): group material and domain controls Preserve the original M2a working slice from prerequisite 7318023 without rewriting its evidence. The accidental broad-filter fixture execution and automatic output teardown are recorded as an incident, not scientific acceptance. Original patch SHA256 0c8b8d4e4eb87f761ff94f998f58548888ba23aa6b40414605652044816c2a6b; exact tree8740bf155b3db1f3c44e109277605b9411f459fe. (cherry picked from commit 1b5842968fcec2d83472ab1aada3c0ef30bb5d8c) --- docs/plans/ui-refinement/execution-ledger.md | 44 ++ docs/plans/ui-refinement/issues.md | 2 + docs/plans/ui-refinement/plan.md | 58 +- .../reports/m2a-material-domain.md | 126 ++++ .../ui-refinement/reports/m2a-plan-review.md | 74 ++ .../reports/m2a-source-review.md | 111 +++ src/bin/desktop/settings.rs | 694 ++++++++++++++++-- 7 files changed, 1061 insertions(+), 48 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/m2a-material-domain.md create mode 100644 docs/plans/ui-refinement/reports/m2a-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/m2a-source-review.md diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 034ed509..e8fb5048 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -167,3 +167,47 @@ /tmp/rheon-ui-contract-* and /tmp/rheon-ui-supervisor-tests*.log. These do not constitute genuine cancellation, kernel cgroup/tmpfs integration or native accessibility verification. Existing native result remains scoped to426fd25. + +## 2026-10-20 — read-only containment refusal and independent M2a + +- Parent requested inspection of already supplied containment, with no + provisioning, delegation/permission changes or genuine execution. Cgroup2 is + present but mounted read-only, root/sole child `init` are not writable, `init` + is populated and lacks required caps. Existing tmpfs capacity9,441,337,344 bytes + and2,305,014 inodes, no `noswap`/exclusive lease, does not meet the reviewed + dedicated32 MiB/1,024-inode contract. Only live cancellation is parked; no + alternate supervisor, cap changes or genuine test execution. +- Admitted independent M2a settings.rs-only production refinement from7318023. + Original commits/branches and old transfer/native evidence remain intact. No + new commit or publication. Stable thermal disclosures retain current°C headers, + gas pressure/model limits stay visible, property help shares its hover-copy + owner, full selected captions and popup options wrap within available width. + No shared quantity-helper, physics/default, launch, worker, recording, + Inspector or comparison source edits. +- Independent admission review required stable IDs, popup wrapping and shared + copy, all incorporated. Narrow-form failures found a density-unit selector + minimum of100px overflowing Air rows; bounded80px minimum passes all three unit + choices at240/280px, without truncating units or changing conversion. +- Keyboard testing exposed existing forward-Tab Air density/molar-mass rounding. + UI-5 records exact before/after SI values and pinned egui lost-focus reparse. + General keyboard value preservation remains unresolved. The help test uses + actual isolated reverse-Tab reachability plus Enter/Space/Tab and strict config + equality; it does not establish full-app/native focus or accessibility. +- Test-selection incident: the lead accidentally ran substring filter `ui_`, + which selected five existing numerical fixture tests alongside UI tests. It + completed19pass/1ignored in5.85s before detection. This violated the instruction; + it was disclosed promptly and not repeated or counted as inert evidence. + Exact tests, numerical scope, fixture-owned teardown and retained complete log + are in [M2a report](reports/m2a-material-domain.md). Genuine cancellation stayed + ignored. No manual cleanup or workload authority expansion followed. +- Final narrowly inspected checks:5 settings tests and9 setup UI tests passed; + locked affected desktop check, strict desktop/test Clippy, Rust formatting and + diff whitespace passed. Clippy's initial test-initializer lint was corrected + without allowances. Logs `/tmp/rheon-ui-m2a-final-*.log`; earlier failures + retained as `/tmp/rheon-ui-m2a-tests*.log`. These checks are headless only and + do not erase the execution incident or close M1/A4/A5. +- Current production/test file SHA256 + `0937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102`. + No local display; existing426fd25 native results remain scoped to that source. + M2a handoff needs exact-patch idle forms/menus/thermal/help at both window + sizes/scales and actual quality85 JPEGs; no solver/cancel workload is needed. diff --git a/docs/plans/ui-refinement/issues.md b/docs/plans/ui-refinement/issues.md index ec41a0c6..7e1adf32 100644 --- a/docs/plans/ui-refinement/issues.md +++ b/docs/plans/ui-refinement/issues.md @@ -6,6 +6,8 @@ | UI-2 | No application standards-engine tool; UI lead | Current pinned standards read directly; no automated conformance certification. Revisit if authoritative application tool becomes available. | | UI-3 | Separate local Inspector slice/dependency and test-only Clippy fix; Inspector validator | Excluded from M1. Reconcile final validated source before M4; parent relays coordination. | | UI-4 | 40% visible-word/usability target needs current matched native baseline; native owner | No result claimed. Preserve warning/numerical context regardless of count; evaluate full staged refinement. | +| UI-5 | Forward keyboard traversal of existing numeric editors can round canonical Air density/molar mass without typed input | Open. M2a help has isolated reverse-Tab reachability evidence only. Reproduce and repair in a separately admitted numeric-editor slice; see M2a report for exact values/source cause. Do not claim general keyboard value preservation. | +| UI-6 | Live M1 cancellation lacks the reviewed existing execution containment | Parked only for live execution: read-only cgroup mount, no exclusive writable leaf, no dedicated bounded noswap tmpfs. Do not provision or weaken the supervisor. | Resolved within M1: active-rerun UI identity mismatch, long-status keyboard access, sealed-domain caption preservation and first-frame nested-panel overflow. See diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 39753fae..74785a8c 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Focused desktop UI refinement **Plan status:** `Active` -**Current phase:** M1 partial native evidence and remaining fixture checks -**Next slice:** M1 — reviewed supervisor contract; genuine execution remains unadmitted +**Current phase:** M1 evidence gaps parked; independent M2 material/domain refinement +**Next slice:** M2a — source review and exact-patch idle native handoff **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -62,7 +62,7 @@ M4 must revalidate that work's final source before admitting Inspector edits. | Milestone | Goal and exact production write set | Dependency / gate | State | | --- | --- | --- | --- | | M1 | One selected setup workflow and persistent run controls. `src/bin/rheon-desktop.rs` (sidebar, presentation methods, UI state/tests only), `src/bin/desktop/configured_controls.rs` (form/action split and labels only), optional `src/bin/desktop/setup_ui_tests.rs` | Independent plan/design review; A1–A3 plus A6 supporting checks; A4 native evidence remains required | Verifying | -| M2 | Compact optional setup, aligned fields and keyboard-accessible help. `src/bin/desktop/{settings,collision_ui,emitter_controls}.rs`; optional new `src/bin/desktop/form_controls.rs` | M1 native feedback; preserve policy and unit editing; A4–A5 | Planned | +| M2 | Compact optional setup, aligned fields and keyboard-accessible help. `src/bin/desktop/{settings,collision_ui,emitter_controls}.rs`; optional new `src/bin/desktop/form_controls.rs` | M1 idle native feedback received; preserve policy and unit editing; A4–A5 | M2a source verified, native pending; remaining slices planned | | M3 | Camera/transport consistency. `src/bin/desktop/{viewport,particle_view,engineering,transport_view}.rs`; optional `src/bin/desktop/playback_controls.rs` | M1/M2; separately re-admit exact main-file playback regions if needed; focus and session-ownership evidence | Planned | | M4 | Promote existing D1 comparison/archive results. `src/bin/desktop/{comparison,comparison_field_view,archive_view,run_set_view,inspector}.rs` and `src/bin/desktop/ai_comparison/{mod,diagnostic_view}.rs` | M3 and final Inspector revision reconciled; re-admit exact composition-root regions; saved-fixture integrity/absence evidence | Planned | @@ -233,3 +233,55 @@ teardown. Only disposable owned test processes/descendants can be terminated. Independent source review and non-numerical tests precede any future admission. Long-status headless stress can proceed now; native accessibility, genuine worker cancellation and whole M1 acceptance remain open. M2–M4 stay Planned. + +## Read-only containment preflight and M2a admission — 2026-10-20 + +Parent authorizes proceeding with the next independent UI milestone while parking +only live cancellation. Read-only preflight confirms cgroup2 is mounted read-only; +the only visible child (`init`) is populated and not writable. No exclusively +delegated empty writable leaf exists. Existing `/tmp` and `/dev/shm` tmpfs mounts +have capacity 9,441,337,344 bytes and 2,305,014 inodes, no `noswap` option or +exclusive lease, exceeding the reviewed 32 MiB/1,024-inode contract. No mounts, +delegation, limits, permissions or process membership changed; no genuine test +ran. The reviewed supervisor remains unchanged. See +[preflight and M2a evidence](reports/m2a-material-domain.md). + +Serial M2a production write set: **only** `src/bin/desktop/settings.rs`, including +its UI-only tests. Authored plan/review/evidence files may change. Base is local +`7318023f04946171d7909119300af76e455c2926`; preserve every existing commit and +remote branch, with no publication. M1 is not accepted. No Inspector, D1, +collision/emitter implementation, main-file launch routing, worker, solver, +recording or numerical default changes are admitted. + +Keep material state/density/viscosity/surface tension/wall drag, gas molar mass and +reference pressure directly visible. Group temperature editing and thermal +explanations under explicit default-closed disclosures, whose headers retain the +current temperature in °C. Keep phase/no-boiling/no-freezing/no-elastic-slime +limitations outside disclosures. Add a named keyboard-reachable Property help +disclosure containing the existing hover explanations, preserving their meaning. +Bound all material/domain preset selector minimum widths and wrap full selected +labels without truncation. Preserve widget IDs for existing selectors, units, +ranges, conversion, defaults and SI owners; hiding/reopening inputs must not +change values. Do not alter the shared quantity helper in this slice. + +Acceptance evidence: real egui pointer/keyboard input opens and closes both +thermal disclosures and Property help; disclosure operation/idle painting retains +all configuration values; values edited through reopened controls preserve SI +conversion; all preset/fill choices fit 240/280 px form widths; essential model +limitations and gas reference pressure remain emitted when thermal help is closed. +Run focused material and existing setup UI tests, formatting, affected locked +compile and strict Clippy; obtain independent source/design review. These tests +are headless and cannot accept native appearance, accessibility or the 40% word +target. Native handoff requires idle material/domain screens, expanded thermal/help +states and dropdowns at both supported window sizes/scales, exact source seal and +quality-85 actual JPEGs; no run/cancel workload is needed. Rollback is the isolated +M2a working diff; no migration or saved-data operation is involved. + +Known M2a evidence limit: existing forward-Tab numerical-editor rounding is open +as UI-5, with exact Air SI values/reproduction in the report. The help test proves +only its isolated reverse-Tab route; no general keyboard value-immutability or +accessibility result is claimed. An accidentally broad `ui_` filter executed five +existing numerical-fixture tests before detection; its full names/effects and log +are recorded in the report. It is not admitted workload or UI acceptance evidence. +Subsequent runtime checks use only the inspected `settings::tests::` and +`setup_ui_tests::ui_setup_` prefixes. The genuine cancellation test remains unrun. diff --git a/docs/plans/ui-refinement/reports/m2a-material-domain.md b/docs/plans/ui-refinement/reports/m2a-material-domain.md new file mode 100644 index 00000000..3d12857b --- /dev/null +++ b/docs/plans/ui-refinement/reports/m2a-material-domain.md @@ -0,0 +1,126 @@ +# Containment preflight and M2a material/domain refinement + +Base: `7318023f04946171d7909119300af76e455c2926`, tree +`258d1c744f046a1b56b4ec6dab2e4edb3e9133cb`. This is an uncommitted local +continuation. Existing commits, remote branch and retained native artifacts are +preserved. No new commit/publication or native session. An unintended broad test +filter did execute numerical fixtures; its exact scope is recorded below. + +## Read-only containment result: live cancellation parked + +Inspected `/proc/self/{cgroup,mountinfo}`, `/proc/swaps`, visible cgroup files, +filesystem capacity/inodes and inherited rlimits with ordinary read-only calls. +No write probe, cgroup migration, mount, provisioning or permission expansion. + +| Prerequisite | Observed | Result | +| --- | --- | --- | +| Linux cgroup v2 and cpu/memory/pids controllers | `/sys/fs/cgroup` is cgroup2; controllers visible | Present, insufficient alone | +| Exclusive empty writable delegated leaf, writable procs/kill | Mount is **read-only**; root and sole visible child `init` not writable. `init` is populated, has no children and no enabled child controllers | Missing; no usable delegated leaf or exclusive reservation | +| Required CPU/memory/process caps | Root cpu.max `400000 100000`, memory.max `17179869184`, swap.max `max`, pids.max `max`; `init` cpu/memory/swap/pids are `max`. cpu.max.burst is 0 at root | Required contract caps absent; do not change them | +| Dedicated empty tmpfs, `noswap`, <=32 MiB and <=1,024 inodes | Existing `/tmp` and `/dev/shm` are tmpfs, capacity **9,441,337,344 bytes**, **2,305,014 inodes**, no `noswap` mount option. `/tmp` is shared; no exclusive reservation supplied for either | Missing; spare capacity is not a bounded dedicated mount | +| Existing swap configuration | `/proc/swaps` lists no active swap | Does not satisfy the required mount-level `noswap` condition | +| Per-process limits can remain unchanged or be lowered | CPU/AS/FSIZE unlimited; NOFILE 16,384; CORE soft 0. No lowering attempted | No tighter inherited-limit conflict observed; genuine execution not admitted | +| Exact source/test contract and sealed developer artifact | Existing source7318023, ignored genuine case, reviewed supervisor/seal retained from previous checkpoint | Preserved; no executable invoked by this preflight | + +Concrete blocker: this environment supplies neither the writable exclusive +cgroup leaf nor the dedicated bounded tmpfs required by the reviewed contract. +**Only live cancellation is parked.** No alternative supervisor or weaker +fallback is designed. It can resume only in an already authorized environment +with those exact resources supplied and verified. M1 remains unaccepted. + +`DISPLAY` and `WAYLAND_DISPLAY` are unset. No current native inspection or new +JPEG is claimed; the existing426fd25 native evidence retains its original scope. + +## Independent M2a slice + +Only production file: `src/bin/desktop/settings.rs`. Material/domain preset +selectors retain their IDs, full meanings and options while bounding selected +text and opened menus to available width. Temperature editors move under named +default-closed disclosures with current °C in their stable-ID headers. Model +limits and gas reference pressure stay outside. Property help exposes existing +hover explanations through an ordinary named disclosure, with actual keyboard +input covered independently of native accessibility claims. + +All units, values, ranges, default physics, conversions and `.changed()` guards +remain owned by their existing controls. The shared quantity helper, launch, +worker, cancellation, recording, Inspector and D1 are unchanged. No generic form +framework or smaller typography is introduced. + +## Verification and native handoff + +Five focused `settings::tests::` tests and nine `setup_ui_tests::ui_setup_` tests +pass. Locked affected desktop compilation, strict affected desktop/test Clippy, +Rust formatting and diff whitespace checks pass. Final logs are +`/tmp/rheon-ui-m2a-final-{material-tests,setup-tests,clippy,check,fmt,diff-check}.log`. +The reviewed production/test file SHA256 is +`0937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102`. +[Independent admission review](m2a-plan-review.md) and +[source review](m2a-source-review.md) found no remaining newly introduced material +source issue under the explicit evidence limits below. Neither review accepts +native/general keyboard usability or excuses the test-selection incident. + +Initial narrow-form regressions exposed the gas density-unit selector's +100px default minimum; reducing that minimum to80px retains complete unit labels +and fits the row. All three unit choices and both form widths are covered. +Keyboard focus traversal needs a settling frame in egui; actual input remains +the test oracle, without forced focus. Failure logs are preserved. + +### Open numeric-keyboard defect (UI-5) + +Forward Tab through the Air material editors without typing changed density +from `1.2041288413018594` to `1.2041300000000001` kg/m³ and molar mass from +`0.02896546` to `0.028965499999999998` kg/mol in the original forward-traversal +test (`/tmp/rheon-ui-m2a-tests-4.log`). The pre-slice source already used the same +eight-decimal formatting/conversion path; pinned egui0.33.3 `drag_value.rs` +reparses cached formatted text on lost focus. This is an existing numeric-editor +defect, not deliberate editing or a harmless test artifact. It remains open; +repair needs an explicit shared-editor admission and independent regression. + +The focused help test now uses actual Tab to the first preset selector, then +Shift+Tab to the final Property help disclosure, Enter to open and Space to close, +then Tab away. This proves that isolated form route and unchanged configuration +for it; it does not prove full-application focus order, arbitrary traversal value +preservation, native keyboard behavior or accessibility. Exact strict equality +is retained; Air values were not normalized and tolerances were not weakened. + +### Test-selection incident + +The lead accidentally used the substring filter `ui_`, which matches full test +paths/names, including existing numerical fixture tests. It completed before +detection:19 passed,1 ignored,5.85s. The genuine supervised cancellation test was +ignored and did not run. The numerical fixtures selected unintentionally were: + +- `archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity`: + 3×4×5 carrier, two0.003s steps, pressure cap2000 iterations. +- `inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step`: + 4³ particle samples, three1e-5s steps, max64 particles. +- `inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors`: + 3×2×4 carrier, two0.01s steps, pressure cap1000 iterations. +- `engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles`: + default12×8×6 static-obstacle pressure projection,0.05s interval,2000-iteration cap. +- `configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources`: + existing native/configured2³ comparison worker fixture, three1/16s requested + steps per member, stdlib Python analytic pressure-adapter fixture. + +This violated the no-numerical-execution instruction. The lead disclosed it +promptly, inspected the selected source paths, and did not rerun the broad +filter. Existing successful tests performed their own disposable-output teardown; +already removed fixture bytes cannot be claimed preserved. The complete test log +is retained at `/tmp/rheon-ui-m2a-all-ui-tests.log`. No manual cleanup, new +supervisor, live cancellation claim or acceptance credit comes from this run. +Subsequent runtime checks use only the explicitly inspected inert module/test +prefixes named above; compile/lint commands do not execute tests. + +For native validation, apply the exact final working patch to7318023 and verify +all file hashes before building. No workload is required. At1250×820 and900×650, +100% and150% UI scale: open Fluid material and Domain & environment, inspect all +long preset/fill dropdowns, open/close both thermal sections, edit/reopen °C +values, inspect gas reference pressure, and Tab/Shift+Tab to Property help and +activate it using Enter/Space. Verify values/units, scroll access, full captions +and persistent main actions. Preserve/report known UI-5 if reproducing general +numeric traversal; do not treat it as a new M2a value-preservation pass. Preserve +actual quality85 JPEGs labelled with exact +source/patch identity and state. Do not substitute historical captures, screenshots +from another revision or headless tests. Active cancellation is a separate parked +test and is not part of this idle native handoff. No 40% word reduction or +representative user success is asserted. diff --git a/docs/plans/ui-refinement/reports/m2a-plan-review.md b/docs/plans/ui-refinement/reports/m2a-plan-review.md new file mode 100644 index 00000000..cb2e4736 --- /dev/null +++ b/docs/plans/ui-refinement/reports/m2a-plan-review.md @@ -0,0 +1,74 @@ +# Independent M2a admission review + +Reviewed 2026-10-20 by `ui_plan_review`, against local base +`7318023f04946171d7909119300af76e455c2926` and the current M2a admission in +`plan.md`. Read the baseline material/domain forms, material preset/SI owners, +existing egui input tests, and egui 0.33.3 disclosure/ComboBox behavior. +Only this non-authorizing review report was authored. + +## Disposition + +M2a is an independently useful, reversible presentation slice while M1's live +cancellation and native accessibility gaps remain parked. Its production write +set is correctly limited to `src/bin/desktop/settings.rs`; no worker, supervisor, +launch, numerical or Inspector edits are needed. Preserve M1 as unaccepted and +keep all existing commits, native evidence and branch identities intact. + +No missing authority prevents this bounded implementation. Incorporate the +following concrete design conditions and focused inert checks. Native appearance, +focus visibility and the 40% word-reduction target remain separate unmet claims. + +## Findings and conditions + +| ID | Severity | Condition and evidence | +| --- | --- | --- | +| M2P1 | Medium | Use explicit stable disclosure IDs independent of current temperature text, distinct for fluid and domain. Egui derives the default ID from the title, so a dynamic °C title otherwise changes disclosure state when a value changes. Test edit while open, repaint, close/reopen and verify retained value plus stable disclosure state. The in-progress diff already uses explicit IDs; retain them. | +| M2P2 | Medium | Bound opened menu labels as well as selected captions. Egui 0.33.3 `ComboBox` sets popup content to `TextWrapMode::Extend` internally; wrapping only `selected_text` does not satisfy the declared all-preset/fill-choice width claim. Override wrapping inside the relevant menu closures or render bounded full option galleys. Exercise actual open menus at 240/280 logical-pixel form widths, including Slime/Syrup and the edited selected caption. | +| M2P3 | Low | Give the help copy one local owner. Property-help text should reuse the existing density, viscosity, surface-tension, wall-drag and thermal explanation content rather than creating separately maintained definitions. Shared local constants suffice; no framework/helper module is necessary. | + +No numerical redesign is needed. Preserve `.changed()`-guarded conversion: +display °C to stored Kelvin by adding 273.15, density display-unit factor, +gas molar-mass g/mol to kg/mol division by 1000, existing ranges and edit speeds. +The thermal header is a readout of current configuration, not a new stored +temperature. Do not round the canonical value to the header's display precision +or write back during idle paint/disclosure toggling. + +Gas molar mass and reference gas pressure must remain outside the collapsed +fluid thermal section and be calculated from the current canonical material. +Phase limitations and fixed-viscosity/thermal meaning remain available as +specified. Ambient wall temperature and wall heat-exchange editing remain +distinct from emitted-fluid temperature; preserve their existing SI owners. +Keep gravity, fill choices and initial mass semantics unchanged. + +## Proportionate evidence + +- Use actual egui pointer input for each disclosure and actual keyboard + Tab/Shift+Tab plus Space/Enter activation where feasible. Direct + `request_focus` proves focused activation only, not keyboard reachability; + label the result accordingly and retain the native gap. +- Check values remain unchanged through idle closed/open paint, close/reopen, + help navigation and preset-menu dismissal. A deliberate preset selection + must still replace material values exactly as its existing owner defines. +- Perform an actual text edit after opening each thermal disclosure and verify + independent expected SI results; a source-formula-only assertion would miss + a disconnected edit control or changed-state error. +- Verify emitted labels include model limitations, current °C and gas reference + pressure when thermal sections are closed. Inspect actual text/control bounds + for the complete selected and popup labels, rather than asserting only that a + widget or string exists. +- Reuse existing test mechanisms. Focused settings/setup tests, formatting, + locked affected compile and strict Clippy supply the selected local evidence. + No solver/model/evaluator/scientific or genuine ignored-cancellation test is + needed or authorized for M2a. + +The plan properly limits the earlier containment preflight to read-only +observations and does not treat unavailable cgroup/tmpfs prerequisites as +permission to provision or weaken limits. This review does not independently +verify the recorded host facts and does not authorize live cancellation. + +After source checks, hand off exact-revision idle material/domain screens, +expanded thermal/help sections and dropdowns to the existing native owner at +both supported sizes/scales. Preserve reported native evidence as scoped to its +actual revision/states. No native or assistive-technology conformance result, +standards-engine certificate, publication or whole-plan acceptance follows from +this plan review. diff --git a/docs/plans/ui-refinement/reports/m2a-source-review.md b/docs/plans/ui-refinement/reports/m2a-source-review.md new file mode 100644 index 00000000..f2a95d85 --- /dev/null +++ b/docs/plans/ui-refinement/reports/m2a-source-review.md @@ -0,0 +1,111 @@ +# Independent M2a source review + +Reviewed 2026-10-20 against clean prerequisite +`7318023f04946171d7909119300af76e455c2926`, the M2a admission in `plan.md` +and `m2a-plan-review.md`. Reviewed settings SHA256: +`0937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102`. +Only this report was authored by the reviewer. No runtime tests, numerical, +model or native workloads were executed by this reviewer. + +## Disposition + +No remaining newly introduced material source issue in the bounded M2a +presentation diff. Production changes remain confined to +`src/bin/desktop/settings.rs`; this finding does not accept native appearance, +general keyboard value preservation, accessibility, whole M1/M2 or the word-count +target. The verification incident below also prevents describing the overall +continuation as having stayed within its no-numerical-execution instruction. + +## Invariants and design conditions + +- `preset_text` and the material/fill/ambient ComboBoxes bound complete selected + captions using existing button typography, and wrap opened option labels. + Existing selector salts remain `material-preset`, `density-unit`, + `domain-fill` and `ambient-material`. The density selector's minimum changes + from the default 100 px to 80 px after a real narrow-form failure; complete + unit labels remain available. No icon substitution or font reduction. +- `fluid` retains state, density, viscosity, liquid-only surface tension, wall + drag, gas molar mass/reference pressure and the phase/model limitation outside + optional disclosures. Gas reference pressure still reads canonical density, + gas constant and temperature. The thermal title reports current °C without + writing its rounded display back into configuration. +- Explicit `fluid-temperature`, `domain-thermal-environment` and + `material-property-help` salts separate persistent disclosure state from + changing captions. Egui 0.33.3 headers default closed. Editing temperature + therefore does not replace the disclosure's identity or close it. +- Existing `.changed()` guards, temperature ranges/speeds and °C-to-Kelvin + conversion remain intact. Density display-unit factors, g/mol-to-kg/mol + conversion, gravity, volume/initial mass, fill semantics and defaults remain + unchanged. The shared `quantity` helper and numerical/worker/recording owners + are untouched. +- The plan review's copy-ownership condition is resolved: local constants share + density, viscosity, tension, wall-drag and wall-thermal meanings between hover + and visible help. Fixed-viscosity/thermal meaning remains in the fluid thermal + disclosure; the phase/no-boiling/no-freezing/no-elastic-slime limitation stays + directly visible. + +## Focused evidence + +Inspected the final focused logs: five `settings::tests::` tests and nine +`setup_ui_tests::ui_setup_` tests pass. Final compile, strict Clippy and formatting +logs show success; reviewer `git diff --check` passes. The reviewer did not rerun +runtime tests or broaden their selection. + +The settings tests use actual egui pointer events to toggle both thermal +disclosures, edit 20 °C to 37.25 °C, check the independent expected 310.4 K, +retain open state across the dynamic-title change, close/reopen without +value loss, and edit again to 38.5 °C/311.65 K after reopening. Actual +material/ambient preset selection resets only the existing +material owner; fill selection and all three density-unit display choices retain +the expected other settings. All presets, edited captions and opened menus are +checked at 240/280 logical-pixel form widths, with explicit assertions that +non-selected popup options actually appear. The help test verifies essential +model/gas-pressure text with optional sections closed, then actual keyboard +activation/closure and exit through the limited route described below. + +These are isolated egui layout/input results, not rendered native screens, +physical-scale measurements or proof of the application's complete focus order. +Current-revision idle native validation at both supported sizes/scales remains +pending, as do representative usability and matched-state word measurements. + +## Open preexisting numeric-navigation defect: UI-5 + +The original forward-Tab test exposed canonical Air density changing from +`1.2041288413018594` to `1.2041300000000001` kg/m³ and molar mass from +`0.02896546` to `0.028965499999999998` kg/mol without typing. The exact failure is +retained in `/tmp/rheon-ui-m2a-tests-4.log`. + +The prerequisite already uses the same eight-decimal DragValue formatting and +SI conversions. Pinned egui `drag_value.rs` reparses cached formatted text on +lost focus. This is an actual existing numeric-editor defect, not deliberate +editing or a harmless test artifact. It is tracked as UI-5; fixing the shared +editor is outside this slice and needs a separate bounded admission/regression. + +The passing help test uses actual Tab to the first selector, Shift+Tab around +the isolated form to its final help disclosure, Enter to open, Space to close +and Tab away. Exact configuration equality is retained without normalizing Air +or weakening tolerances. This proves that particular route only. It does not +repair forward traversal or establish an equivalent shortcut in the full app. +Preserve/report UI-5 in native validation; general keyboard value immutability +and accessibility remain unaccepted. + +## Verification-process incident + +The lead disclosed that an earlier `ui_` substring filter selected numerical +Inspector/archive/engineering fixtures and a configured comparison fixture with +a Python analytic pressure-adapter runtime. Its retained log records 19 passed, +one ignored; the genuine cancellation test remained ignored. This execution +violated the no-numerical-execution instruction. It supplies no M2a acceptance +credit and is not authorization for any repetition or broader workload. + +`m2a-material-domain.md` records the exact affected tests, bounds, disclosure and +artifact limits. Some existing tests removed their own disposable outputs before +detection; those bytes cannot be represented as preserved. The broad log remains +at `/tmp/rheon-ui-m2a-all-ui-tests.log`. No reviewer runtime execution, rerun or +cleanup followed. Subsequent successful focused logs are +`/tmp/rheon-ui-m2a-final-material-tests.log` and +`/tmp/rheon-ui-m2a-final-setup-tests.log`; compile/lint evidence is separate. + +Live cancellation remains parked on missing containment prerequisites. This +source review does not independently verify the host preflight or historical +native artifacts, authorize provisioning/publication, or close those gaps. diff --git a/src/bin/desktop/settings.rs b/src/bin/desktop/settings.rs index f651123f..d18d1904 100644 --- a/src/bin/desktop/settings.rs +++ b/src/bin/desktop/settings.rs @@ -2,6 +2,30 @@ use eframe::egui; use rheon::{DomainFill, FluidPhase, FluidPreset, ParticleFluidConfig}; +const DENSITY_HELP: &str = + "Mass per volume. Weight also depends on gravity. 1 g/mL = 1 mg/mm³ = 1,000 kg/m³."; +const VISCOSITY_HELP: &str = "Dynamic viscosity: resistance to shear. This preview uses a Newtonian fluid; slime has no elastic stretching."; +const TENSION_HELP: &str = + "Liquid cohesion approximation; larger values draw free surfaces together."; +const WALL_DRAG_HELP: &str = "Stickiness at walls: tangential velocity damping on contact. Separate from viscosity and surface tension; not a contact-angle model."; +const WALL_HEAT_HELP: &str = "Temperature relaxes toward the wall temperature on contact. Vacuum has no surrounding gas or bulk heat bath."; + +/// Combo widths are minimums in egui; bound the complete label as well. +fn preset_text(ui: &egui::Ui, text: impl Into) -> egui::WidgetText { + text.into() + .into_galley( + ui, + Some(egui::TextWrapMode::Wrap), + (ui.available_width() + - 2.0 * ui.spacing().button_padding.x + - ui.spacing().icon_spacing + - ui.spacing().icon_width) + .max(1.0), + egui::TextStyle::Button, + ) + .into() +} + #[derive(Clone, Copy, PartialEq, Eq)] enum DensityUnit { KilogramsPerCubicMetre, @@ -55,14 +79,19 @@ impl MaterialControls { } pub fn fluid(&mut self, ui: &mut egui::Ui) { let custom = self.config.material != self.preset.material(); - egui::ComboBox::from_id_salt("material-preset") - .selected_text(format!( + let preset_label = preset_text( + ui, + format!( "{}{}", self.preset.label(), if custom { " · edited" } else { "" } - )) - .width(245.0) + ), + ); + egui::ComboBox::from_id_salt("material-preset") + .selected_text(preset_label) + .width(ui.available_width().min(245.0)) .show_ui(ui, |ui| { + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); for preset in FluidPreset::ALL { if ui .selectable_value(&mut self.preset, preset, preset.label()) @@ -72,7 +101,7 @@ impl MaterialControls { } } }); - ui.small("Approximate reference presets; edit values below."); + ui.small("Approximate presets · editable"); let m = &mut self.config.material; ui.horizontal_wrapped(|ui| { ui.label("State"); @@ -80,9 +109,7 @@ impl MaterialControls { ui.selectable_value(&mut m.phase, FluidPhase::Gas, "Gas"); }); ui.horizontal_wrapped(|ui| { - ui.label("Density").on_hover_text( - "Mass per volume. Weight also depends on gravity. 1 g/mL = 1 mg/mm³ = 1,000 kg/m³.", - ); + ui.label("Density").on_hover_text(DENSITY_HELP); let mut density = m.density / self.unit.factor(); if ui .add( @@ -97,6 +124,7 @@ impl MaterialControls { } egui::ComboBox::from_id_salt("density-unit") .selected_text(self.unit.label()) + .width(80.0) .show_ui(ui, |ui| { for unit in [ DensityUnit::KilogramsPerCubicMetre, @@ -108,7 +136,15 @@ impl MaterialControls { }); }); ui.small(format!("{:.3} kg/m³", m.density)); - quantity(ui,"Viscosity",&mut m.viscosity,"Pa·s",0.0001,0.0..=100000.0).on_hover_text("Dynamic viscosity: resistance to shear. This preview uses a Newtonian fluid; slime has no elastic stretching."); + quantity( + ui, + "Viscosity", + &mut m.viscosity, + "Pa·s", + 0.0001, + 0.0..=100000.0, + ) + .on_hover_text(VISCOSITY_HELP); ui.add_enabled_ui(m.phase == FluidPhase::Liquid, |ui| { quantity( ui, @@ -118,24 +154,28 @@ impl MaterialControls { 0.001, 0.0..=10.0, ) - .on_hover_text( - "Liquid cohesion approximation; larger values draw free surfaces together.", - ); + .on_hover_text(TENSION_HELP); }); - quantity(ui,"Wall drag",&mut m.wall_drag,"1/s",0.1,0.0..=1000.0).on_hover_text("Stickiness at walls: tangential velocity damping on contact. Separate from viscosity and surface tension; not a contact-angle model."); + quantity(ui, "Wall drag", &mut m.wall_drag, "1/s", 0.1, 0.0..=1000.0) + .on_hover_text(WALL_DRAG_HELP); let mut temperature = m.temperature_kelvin - 273.15; - if quantity( - ui, - "Fluid temperature", - &mut temperature, - "°C", - 1.0, - -273.14..=4000.0, - ) - .changed() - { - m.temperature_kelvin = temperature + 273.15; - } + egui::CollapsingHeader::new(format!("Temperature · {temperature:.2} °C")) + .id_salt("fluid-temperature") + .show(ui, |ui| { + if quantity( + ui, + "Fluid temperature", + &mut temperature, + "°C", + 1.0, + -273.14..=4000.0, + ) + .changed() + { + m.temperature_kelvin = temperature + 273.15; + } + ui.small("Temperature affects gas pressure and wall exchange; viscosity stays at the entered value."); + }); if m.phase == FluidPhase::Gas { let mut molar_mass = m.molar_mass * 1000.0; if quantity( @@ -156,7 +196,14 @@ impl MaterialControls { )); } ui.small("Phase stays fixed: no boiling, freezing or elastic slime."); - ui.small("Temperature affects gas pressure and wall exchange; viscosity stays at the entered value."); + egui::CollapsingHeader::new("Property help") + .id_salt("material-property-help") + .show(ui, |ui| { + ui.label(format!("Density: {DENSITY_HELP}")); + ui.label(VISCOSITY_HELP); + ui.label(format!("Surface tension: {TENSION_HELP}")); + ui.label(format!("Wall drag: {WALL_DRAG_HELP}")); + }); } pub fn domain(&mut self, ui: &mut egui::Ui, side: &mut f64) { quantity(ui, "Domain side", side, "m", 0.01, 0.001..=100.0); @@ -165,14 +212,19 @@ impl MaterialControls { side.powi(3), side.powi(3) * 1000.0 )); - egui::ComboBox::from_id_salt("domain-fill") - .selected_text(match self.config.fill { + let fill_label = preset_text( + ui, + match self.config.fill { DomainFill::Vacuum => "Empty / vacuum", DomainFill::EmittedFluid => "Filled with emitted fluid", DomainFill::OtherFluid => "Filled with another fluid", - }) - .width(245.0) + }, + ); + egui::ComboBox::from_id_salt("domain-fill") + .selected_text(fill_label) + .width(ui.available_width().min(245.0)) .show_ui(ui, |ui| { + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); ui.selectable_value(&mut self.config.fill, DomainFill::Vacuum, "Empty / vacuum"); ui.selectable_value( &mut self.config.fill, @@ -186,10 +238,12 @@ impl MaterialControls { ); }); if self.config.fill == DomainFill::OtherFluid { + let ambient_label = preset_text(ui, self.ambient_preset.label()); egui::ComboBox::from_id_salt("ambient-material") - .selected_text(self.ambient_preset.label()) - .width(245.0) + .selected_text(ambient_label) + .width(ui.available_width().min(245.0)) .show_ui(ui, |ui| { + ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap); for preset in FluidPreset::ALL { if ui .selectable_value(&mut self.ambient_preset, preset, preset.label()) @@ -212,19 +266,32 @@ impl MaterialControls { ui.small(format!("Initial mass {:.3} kg", side.powi(3) * m.density)); } let mut ambient = self.config.ambient_temperature_kelvin - 273.15; - if quantity( - ui, - "Ambient / wall temp", - &mut ambient, - "°C", - 1.0, - -273.14..=4000.0, - ) - .changed() - { - self.config.ambient_temperature_kelvin = ambient + 273.15; - } - quantity(ui,"Wall heat exchange",&mut self.config.wall_heat_exchange,"1/s",0.1,0.0..=100.0).on_hover_text("Temperature relaxes toward the wall temperature on contact. Vacuum has no surrounding gas or bulk heat bath."); + egui::CollapsingHeader::new(format!("Wall heat · {ambient:.2} °C")) + .id_salt("domain-thermal-environment") + .show(ui, |ui| { + if quantity( + ui, + "Ambient / wall temp", + &mut ambient, + "°C", + 1.0, + -273.14..=4000.0, + ) + .changed() + { + self.config.ambient_temperature_kelvin = ambient + 273.15; + } + quantity( + ui, + "Wall heat exchange", + &mut self.config.wall_heat_exchange, + "1/s", + 0.1, + 0.0..=100.0, + ) + .on_hover_text(WALL_HEAT_HELP); + ui.label(WALL_HEAT_HELP); + }); ui.label("Gravity · m/s²"); for d in 0..3 { quantity( @@ -282,6 +349,543 @@ pub fn duration(seconds: f64) -> String { #[cfg(test)] mod tests { use super::*; + + fn context() -> egui::Context { + let ctx = egui::Context::default(); + ctx.style_mut(|style| style.animation_time = 0.0); + ctx + } + fn frame( + ctx: &egui::Context, + width: f32, + controls: &mut MaterialControls, + side: &mut f64, + domain: bool, + events: Vec, + ) -> egui::FullOutput { + let mut right = 0.0; + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, 1600.0), + )), + events, + ..Default::default() + }, + |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + if domain { + controls.domain(ui, side); + } else { + controls.fluid(ui); + } + right = ui.min_rect().right(); + }); + }, + ); + assert!( + right <= width - 7.0, + "form overflow {right} at width {width}, domain {domain}, preset {:?}: {:?}", + controls.preset, + output + .shapes + .iter() + .filter_map(|s| match &s.shape { + egui::Shape::Text(t) if t.pos.x + t.galley.size().x > width - 7.0 => + Some((t.galley.text(), t.pos.x, t.galley.size().x)), + _ => None, + }) + .collect::>() + ); + output + } + fn text_rect(output: &egui::FullOutput, label: &str) -> Option { + output.shapes.iter().find_map(|s| match &s.shape { + egui::Shape::Text(t) if t.galley.text() == label => { + Some(egui::Rect::from_min_size(t.pos, t.galley.size())) + } + _ => None, + }) + } + fn pointer(pos: egui::Pos2, pressed: bool) -> Vec { + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ] + } + fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + [true, false] + .map(|pressed| egui::Event::Key { + key, + physical_key: None, + pressed, + repeat: false, + modifiers, + }) + .into() + } + fn click( + ctx: &egui::Context, + width: f32, + controls: &mut MaterialControls, + side: &mut f64, + domain: bool, + output: &egui::FullOutput, + label: &str, + ) { + let pos = text_rect(output, label) + .unwrap_or_else(|| panic!("missing {label}")) + .center(); + for pressed in [true, false] { + let _ = frame(ctx, width, controls, side, domain, pointer(pos, pressed)); + } + } + + #[test] + fn ui_material_thermal_disclosures_preserve_values_and_si_editing() { + for domain in [false, true] { + let ctx = context(); + let mut controls = MaterialControls::default(); + let mut side = 1.0; + let before = format!("{:?}", controls.config); + let header = if domain { + "Wall heat · 20.00 °C" + } else { + "Temperature · 20.00 °C" + }; + let label = if domain { + "Ambient / wall temp" + } else { + "Fluid temperature" + }; + let _ = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + assert!(text_rect(&output, label).is_none()); + click( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + &output, + header, + ); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + assert!(text_rect(&output, label).is_some()); + assert_eq!(format!("{:?}", controls.config), before); + click( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + &output, + "20 °C", + ); + let _ = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + vec![egui::Event::Text("37.25".into())], + ); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + key(egui::Key::Enter, Default::default()), + ); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + let temperature = if domain { + controls.config.ambient_temperature_kelvin + } else { + controls.config.material.temperature_kelvin + }; + assert!( + (temperature - 310.4).abs() < 1e-10, + "Celsius edit retained in kelvin" + ); + assert!( + text_rect(&output, label).is_some(), + "editing header value must not close disclosure" + ); + let header = if domain { + "Wall heat · 37.25 °C" + } else { + "Temperature · 37.25 °C" + }; + let edited = format!("{:?}", controls.config); + click( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + &output, + header, + ); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + assert!(text_rect(&output, label).is_none()); + click( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + &output, + header, + ); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + assert!(text_rect(&output, label).is_some()); + assert_eq!(format!("{:?}", controls.config), edited); + click( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + &output, + "37.25 °C", + ); + let _ = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + vec![egui::Event::Text("38.5".into())], + ); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + domain, + key(egui::Key::Enter, Default::default()), + ); + let output = frame(&ctx, 240.0, &mut controls, &mut side, domain, vec![]); + let temperature = if domain { + controls.config.ambient_temperature_kelvin + } else { + controls.config.material.temperature_kelvin + }; + assert!((temperature - 311.65).abs() < 1e-10); + assert!(text_rect(&output, label).is_some()); + assert_eq!(side, 1.0); + } + } + + #[test] + fn ui_material_help_is_tab_reachable_and_model_limits_remain_visible() { + let ctx = context(); + let mut controls = MaterialControls { + preset: FluidPreset::Air, + ..Default::default() + }; + controls.config.material = controls.preset.material(); + let before = format!("{:?}", controls.config); + let mut side = 1.0; + let mut output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + assert!( + text_rect( + &output, + "Phase stays fixed: no boiling, freezing or elastic slime." + ) + .is_some() + ); + assert!(output.shapes.iter().any(|s| matches!(&s.shape, egui::Shape::Text(t) if t.galley.text().starts_with("Reference gas pressure ")))); + let help = "Density: Mass per volume. Weight also depends on gravity. 1 g/mL = 1 mg/mm³ = 1,000 kg/m³."; + assert!(text_rect(&output, help).is_none()); + // Reach help through the actual reverse tab order, without focusing numeric + // editors. General numeric-focus rounding is a separate existing UI issue. + let _ = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + false, + key(egui::Key::Tab, Default::default()), + ); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + false, + key( + egui::Key::Tab, + egui::Modifiers { + shift: true, + ..Default::default() + }, + ), + ); + output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + let focused = ctx + .memory(|m| m.focused()) + .and_then(|id| ctx.read_response(id)) + .expect("help has keyboard focus"); + assert!( + focused + .rect + .contains(text_rect(&output, "Property help").unwrap().center()) + ); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + false, + key(egui::Key::Enter, Default::default()), + ); + output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + assert!(text_rect(&output, help).is_some()); + let help_id = ctx.memory(|m| m.focused()).unwrap(); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + false, + key(egui::Key::Space, Default::default()), + ); + output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + assert!(text_rect(&output, help).is_none()); + let _ = frame( + &ctx, + 240.0, + &mut controls, + &mut side, + false, + key(egui::Key::Tab, Default::default()), + ); + let _ = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); + assert_ne!(ctx.memory(|m| m.focused()), Some(help_id)); + assert_eq!(format!("{:?}", controls.config), before); + } + + #[test] + fn ui_material_selection_updates_only_its_existing_owner() { + for width in [240.0, 280.0] { + for domain in [false, true] { + let ctx = context(); + let mut controls = MaterialControls::default(); + controls.config.fill = DomainFill::OtherFluid; + let mut expected = controls.config; + let mut side = 1.0; + let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let label = if domain { + "Air · 20 °C" + } else { + "Water · 20 °C" + }; + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + label, + ); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + "Mercury · 20 °C", + ); + let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + if domain { + expected.ambient_material = FluidPreset::Mercury.material(); + } else { + expected.material = FluidPreset::Mercury.material(); + } + assert_eq!(format!("{:?}", controls.config), format!("{expected:?}")); + if domain { + for (label, fill) in [ + ("Filled with emitted fluid", DomainFill::EmittedFluid), + ("Empty / vacuum", DomainFill::Vacuum), + ] { + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let selected = if controls.config.fill == DomainFill::OtherFluid { + "Filled with another fluid" + } else { + "Filled with emitted fluid" + }; + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + selected, + ); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + label, + ); + let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + expected.fill = fill; + assert_eq!(format!("{:?}", controls.config), format!("{expected:?}")); + } + } else { + for unit in [ + DensityUnit::KilogramsPerCubicMetre, + DensityUnit::MilligramsPerCubicMillimetre, + ] { + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let selected = controls.unit.label(); + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + selected, + ); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + unit.label(), + ); + let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + assert_eq!(controls.unit.label(), unit.label()); + assert_eq!(format!("{:?}", controls.config), format!("{expected:?}")); + } + } + } + } + } + + #[test] + fn ui_material_presets_and_open_menus_fit_narrow_forms() { + for width in [240.0, 280.0] { + for domain in [false, true] { + for preset in FluidPreset::ALL { + let ctx = context(); + let mut controls = MaterialControls { + preset, + ambient_preset: preset, + ..Default::default() + }; + controls.config.material = preset.material(); + controls.config.material.viscosity += 0.0001; // longest edited caption too + controls.config.fill = DomainFill::OtherFluid; + controls.config.ambient_material = preset.material(); + let before = format!("{:?}", controls.config); + let mut side = 1.0; + let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let label = if domain { + "Filled with another fluid".to_owned() + } else { + format!("{} · edited", preset.label()) + }; + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + &label, + ); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + assert!( + text_rect( + &output, + if domain { + "Empty / vacuum" + } else { + "Water · 20 °C" + } + ) + .is_some(), + "preset/fill menu actually opened" + ); + for shape in &output.shapes { + if let egui::Shape::Text(t) = &shape.shape { + assert!( + t.pos.x + t.galley.size().x <= width + 0.5, + "menu text overflow: {}", + t.galley.text() + ); + } + } + assert_eq!(format!("{:?}", controls.config), before); + let _ = frame( + &ctx, + width, + &mut controls, + &mut side, + domain, + key(egui::Key::Escape, Default::default()), + ); + if domain { + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + click( + &ctx, + width, + &mut controls, + &mut side, + domain, + &output, + preset.label(), + ); + let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); + let other = if preset == FluidPreset::Water { + "Mercury · 20 °C" + } else { + "Water · 20 °C" + }; + assert!( + text_rect(&output, other).is_some(), + "ambient menu actually opened" + ); + for shape in &output.shapes { + if let egui::Shape::Text(t) = &shape.shape { + assert!( + t.pos.x + t.galley.size().x <= width + 0.5, + "ambient menu overflow: {}", + t.galley.text() + ); + } + } + } + } + } + } + } #[test] fn mass_density_units_use_independent_si_conversion() { assert_eq!(DensityUnit::GramsPerMillilitre.factor(), 1000.0); From 4604182a9d02ad239b5f2f28a306dbf7adb89d72 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 22:49:20 +0000 Subject: [PATCH 07/25] fix(desktop): preserve material precision during focus Focused numeric fields use round-trip text without changing unchanged canonical SI values. Preserve units, explicit-edit ranges and transactional ownership. Five individually enumerated and inspected exact UI-only tests passed; independent source review is included. UI-5 was observed after M2a; no untouched-baseline reproduction is claimed. Exact frozen precision tree3e1fd57146e2f1a02abe22a09ac317497f39d738. (cherry picked from commit 11415a5000dbd223bcdbfbd5afdd5b5465f60845) --- docs/plans/ui-refinement/execution-ledger.md | 28 ++ docs/plans/ui-refinement/issues.md | 2 +- docs/plans/ui-refinement/plan.md | 32 +- .../reports/ui5-precision-repair.md | 103 +++++ .../reports/ui5-precision-review.md | 105 +++++ src/bin/desktop/settings.rs | 360 +++++++++++++++++- 6 files changed, 618 insertions(+), 12 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/ui5-precision-repair.md create mode 100644 docs/plans/ui-refinement/reports/ui5-precision-review.md diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index e8fb5048..d5008041 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -211,3 +211,31 @@ No local display; existing426fd25 native results remain scoped to that source. M2a handoff needs exact-patch idle forms/menus/thermal/help at both window sizes/scales and actual quality85 JPEGs; no solver/cancel workload is needed. + +## 2026-10-20 — isolated UI-5 precision repair + +- Parent reviewed the incident and authorized only the minimal precision repair. + Preserved original uncommitted M2a worktree, packet and all logs; created a + detached `/workspace/Rheon-ui-precision` at7318023 with the exact preserved M2a + patch. No commit or remote mutation. UI-5 was observed after M2a; no pristine + baseline reproduction. Prior cause is inferred from source, correcting the + stronger historical wording without rewriting its preserved evidence. +- Only settings.rs production changes: private round-trip focused DragValue + formatter at density/shared quantity, initial pointer select-all alignment and + density-unit next-row guard. Idle formatter, IDs, parser, ranges, speeds and SI + guards remain unchanged. Shared helper callers include main/emitter controls; + no force physics, numerical defaults, worker/recording or archive changes. +- Read every selected test/helper path; list-only enumeration preceded each final + source execution. Five individually named `--exact` inert UI tests pass. No + prefix/fallback suite, numerical/model fixture or cleanup. Focus-bit regression + fails against preserved M2a production before repair; focused geometry and + click-selection failures are retained and repaired, with strict equality and + original no-Ctrl+A edit regression preserved. Locked check, strict Clippy, + formatting and diff whitespace pass. Full names/logs in the precision report. +- Automatic successful-test teardown had removed the accidental numerical outputs + before detection; no resource measurements/scientific acceptance are claimed. + Incident log hash remains9d1b7245761f461a86dc436f21911f4a5525af3d2b8f7692d6ff8b5e6f2bbba1. + Genuine cancellation stays parked, with no cgroup/mount provisioning or run. +- New scene/timeline direction supersedes continuing old M2–M4 unchanged. Freeze + this precision evidence before admitting new source regions; independent scene + contracts must separate UI ownership from the other task's force domain/adapters. diff --git a/docs/plans/ui-refinement/issues.md b/docs/plans/ui-refinement/issues.md index 7e1adf32..ece6528d 100644 --- a/docs/plans/ui-refinement/issues.md +++ b/docs/plans/ui-refinement/issues.md @@ -6,7 +6,7 @@ | UI-2 | No application standards-engine tool; UI lead | Current pinned standards read directly; no automated conformance certification. Revisit if authoritative application tool becomes available. | | UI-3 | Separate local Inspector slice/dependency and test-only Clippy fix; Inspector validator | Excluded from M1. Reconcile final validated source before M4; parent relays coordination. | | UI-4 | 40% visible-word/usability target needs current matched native baseline; native owner | No result claimed. Preserve warning/numerical context regardless of count; evaluate full staged refinement. | -| UI-5 | Forward keyboard traversal of existing numeric editors can round canonical Air density/molar mass without typed input | Open. M2a help has isolated reverse-Tab reachability evidence only. Reproduce and repair in a separately admitted numeric-editor slice; see M2a report for exact values/source cause. Do not claim general keyboard value preservation. | +| UI-5 | Focus traversal rounded Air density/molar mass without typed input, first observed after M2a; prior mechanism is source-inferred only | Exact-bit UI repair verified in isolated continuation; native/full-app acceptance pending. Original M2a worktree/patch remains unchanged. See [precision report](reports/ui5-precision-repair.md); no untouched-baseline result is claimed. | | UI-6 | Live M1 cancellation lacks the reviewed existing execution containment | Parked only for live execution: read-only cgroup mount, no exclusive writable leaf, no dedicated bounded noswap tmpfs. Do not provision or weaken the supervisor. | Resolved within M1: active-rerun UI identity mismatch, long-status keyboard access, diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 74785a8c..1db70762 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Focused desktop UI refinement **Plan status:** `Active` -**Current phase:** M1 evidence gaps parked; independent M2 material/domain refinement -**Next slice:** M2a — source review and exact-patch idle native handoff +**Current phase:** UI-5 precision-only repair; original M2a worktree preserved +**Next slice:** UI-5 — exact-value focus regression, bounded editor adapter and review **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -285,3 +285,31 @@ existing numerical-fixture tests before detection; its full names/effects and lo are recorded in the report. It is not admitted workload or UI acceptance evidence. Subsequent runtime checks use only the inspected `settings::tests::` and `setup_ui_tests::ui_setup_` prefixes. The genuine cancellation test remains unrun. + +## UI-5 precision-only continuation — 2026-10-20 + +Parent reviewed the test-selection incident and admits only the smallest focus +precision repair. Preserve the original uncommitted M2a worktree and packet: +new detached worktree `/workspace/Rheon-ui-precision` starts at7318023 and applies +the exact preserved M2a patch0c8b8d4e4eb87f761ff94f998f58548888ba23aa6b40414605652044816c2a6b, +whose resulting tree is8740bf155b3db1f3c44e109277605b9411f459fe. No commit or +publication is admitted. Production scope is only settings.rs: one private +DragValue formatting adapter at the density editor and shared quantity helper. +Focused text must roundtrip the exact display value; idle compact formatting, +IDs, parser, ranges, speeds and `.changed()` SI conversion guards stay unchanged. +Shared quantity callers include domain/main/emitter controls; record that reach. + +UI-5 was observed after M2a, not on a tested pristine baseline. The same pinned +format/reparse mechanism in prerequisite source is an inference about prior +cause, not an empirical baseline result. Repair evidence must retain exact-bit +material equality across focus/forward and reverse Tab without typing, and real +edits/range enforcement in SI units. Check focused240/280px geometry, not only +idle labels. No native accessibility/visual acceptance follows from these tests. + +Before runtime: inspect each entire test/helper path, enumerate with list-only, +then execute only an individually named `--exact` UI-only test. No substring or +fallback suite, simulation/model/numerical fixture, file cleanup or genuine +cancellation. Stop if a selection is uncertain. Independent source review is +required. Keep failure logs and original M2a/incident artifacts intact. Automatic +successful-test teardown removed the accidental disposable numerical outputs +before detection; no resource measurements or scientific acceptance are claimed. diff --git a/docs/plans/ui-refinement/reports/ui5-precision-repair.md b/docs/plans/ui-refinement/reports/ui5-precision-repair.md new file mode 100644 index 00000000..dc30cc94 --- /dev/null +++ b/docs/plans/ui-refinement/reports/ui5-precision-repair.md @@ -0,0 +1,103 @@ +# UI-5 focus precision repair + +Original M2a is preserved byte-for-byte in `/workspace/Rheon-ui-hierarchy`, its +65,576-byte patch SHA256 +`0c8b8d4e4eb87f761ff94f998f58548888ba23aa6b40414605652044816c2a6b` +and `/workspace/scratch/rheon-ui-m2a`. This continuation uses a separate detached +worktree `/workspace/Rheon-ui-precision`, HEAD7318023 plus that exact M2a patch +(tree8740bf155b3db1f3c44e109277605b9411f459fe). No commit/publication. + +## Observed fact versus inferred cause + +UI-5 was first observed after the M2a diff in +`/tmp/rheon-ui-m2a-tests-4.log`. No untouched-baseline UI test was run. Source +inspection found the same formatting/conversion path in prerequisite7318023 and +pinned egui0.33.3's focus-loss reparse. A prior mechanism is inferred, not an +empirically demonstrated baseline defect. This report supersedes the stronger +"preexisting defect" wording in the preserved historical M2a reports. + +Observed Air density changed without typing from1.2041288413018594 to +1.2041300000000001 kg/m³; molar mass from0.02896546 to +0.028965499999999998 kg/mol. Full canonical bits must survive focusing and +forward/reverse Tab, not merely remain within a numerical tolerance. + +## Bounded repair and verification procedure + +Only production file: settings.rs. Add one private adapter immediately around +DragValue addition, used by density and the existing quantity helper. Its focused +text must roundtrip the actual display value; retain the ordinary idle formatter, +IDs, parser, speeds, ranges, clamping and SI update guards. No model/solver/worker, +recording or dependency change. The shared quantity helper also serves main +domain/maximum-step/stiffness and emitter properties; the formatting reach is +broader than material fields, although those caller sources are untouched. + +The new tests and selected existing tests were inspected through their complete +helper paths: local context/frame/text/pointer/key/click helpers, MaterialControls +fluid/domain forms, quantity and pure material/config defaults. They do not +construct simulations, Workers, archives or files, spawn processes, or clean up +filesystem outputs. Egui Context/input/paint runs headlessly. Runtime selection +must first be list-only, then one fully qualified `--exact` name per invocation; +stop if any helper/selection is uncertain. No broad filter/fallback suite. + +New regression covers Air plus precise liquid properties, all three density units, +240/280px widths, actual forward/reverse Tab and exact-bit material preservation. +Explicit-edit checks cover density SI conversions, molar-mass g/mol conversion, +out-of-range clamping and invalid text. Existing selected thermal/selection/menu +tests use the same verified inert paths. Focused editor width must be checked, +not only idle captions. These checks cannot accept native appearance, complete +application traversal/accessibility or scientific correctness. + +## Incident evidence remains preserved + +The earlier broad `ui_` test-selection incident is not repeated or reclassified. +**Automatic successful-test teardown removed disposable numerical outputs before +detection.** Their removed bytes and resource use cannot now be reconstructed. +There were no CPU/RSS/disk resource measurements; the19-pass/1-ignored log's5.85s +is harness elapsed time, not scientific acceptance or a containment result. +Log SHA256 remains +`9d1b7245761f461a86dc436f21911f4a5525af3d2b8f7692d6ff8b5e6f2bbba1`. +No manual cleanup, numerical/model test, genuine cancellation, cgroup/mount +provisioning or network change is admitted for this precision continuation. + +## Results and remaining gaps + +Five individually enumerated/selected exact UI-only tests pass, one per command: + +1. `settings::tests::ui_precision_tab_and_focus_preserve_material_bits` +2. `settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges` +3. `settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing` +4. `settings::tests::ui_material_selection_updates_only_its_existing_owner` +5. `settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms` + +List-only log `/tmp/rheon-ui-precision-final-test-list-5.log` enumerated168 names; +the five are an explicit allowlist, not a runtime prefix filter. Final runtime +logs `/tmp/rheon-ui-precision-final-v3-exact-{1,2,3,4,5}.log` each record one pass. +Locked desktop check, strict desktop/test Clippy, formatting and diff whitespace +checks pass (`/tmp/rheon-ui-precision-final-{check,clippy,fmt,diff-check}.log`). +Reviewed source SHA256: +`099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121`. + +The production adapter uses round-trip Debug text only while focused (scientific +notation for tiny values); ordinary idle formatting remains unchanged. Pointer +entry adjusts initial select-all to the complete edit text through public +TextEditState, preserving subsequent caret placement. Density's unit selector +moves to the next row only when the focused value leaves insufficient room. +No persistent canonical/dirty state, replacement parser, or tolerance is added. + +The deciding pre-repair exact test failed material bits against byte-identical +M2a production (`before.log`). Initial full-text geometry overflow is preserved +in `after-1.log`; the initial click-selection regression and test label-lookup +failures remain in separate earlier exact logs. Full precision lengthening exposed +a real click-to-replace regression: the compact button selected fewer characters +than the new editor. It was repaired and the original no-Ctrl+A thermal regression +passes unchanged; the new density edit test also uses ordinary click replacement. +Exact-bit forward/reverse/pointer focus checks include tiny positive viscosity, +both supported narrow form widths and all three density units. Explicit edits +retain SI conversion, ArrowUp, out-of-range clamps and invalid-text refusal. + +Independent [source review](ui5-precision-review.md) supplies its own scoped +disposition; no reviewer runtime tests are needed. Preserve every deciding failure +and final log separately; no log was overwritten to hide a failure. Original +M2a report/review identities remain historical, not rewritten to imply acceptance +of the precision repair. Native focused-field appearance, full application focus, +assistive technology, representative usability and M1/A4/A5 remain unaccepted. diff --git a/docs/plans/ui-refinement/reports/ui5-precision-review.md b/docs/plans/ui-refinement/reports/ui5-precision-review.md new file mode 100644 index 00000000..e69062d8 --- /dev/null +++ b/docs/plans/ui-refinement/reports/ui5-precision-review.md @@ -0,0 +1,105 @@ +# Independent UI-5 precision review + +Reviewed 2026-10-20 in `/workspace/Rheon-ui-precision`, detached prerequisite +`7318023f04946171d7909119300af76e455c2926` plus the preserved M2a overlay. +Final reviewed `src/bin/desktop/settings.rs` SHA256: +`099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121`. +Only this report was authored by the reviewer. No runtime tests, numerical, +model or native workloads were executed by the reviewer. + +## Disposition + +No remaining material source finding in this bounded precision repair. The +reviewed exact-test evidence resolves the observed isolated-form focus-rounding +failure while retaining intentional editing and compact idle formatting. This +does not accept native focused-field appearance, complete application traversal, +assistive technology, scientific correctness or whole M1/M2 acceptance. No later +timeline/scene/property/physics implementation is included in this disposition. + +UI-5 was observed after M2a. The deciding before test exercises unchanged M2a +production with the new regression; it is not an untouched-prerequisite baseline +experiment. The earlier M2a review's stronger “preexisting defect” description +was not empirically established. A similar prior format/reparse mechanism is +source-based inference only. This report corrects that evidence boundary without +rewriting the preserved historical reports. + +## Source and invariant review + +`precise_drag` captures the next automatic ID immediately before adding the +DragValue. In pinned egui 0.33.3, `Ui::add` delegates directly to the widget, and +DragValue uses that same ID and registers focus before calling its formatter. +The callback therefore queries current focus at the correct point, including the +first keyboard-focus frame. It uses round-trip Debug formatting while focused +and the existing style formatter/decimal policy while idle. Scientific notation +avoids very long fixed-decimal strings for tiny valid values. + +Unchanged focused text reparses to the same temporary display value, so existing +`.changed()` guards avoid needless canonical SI writeback. The two insertion +sites retain density factors, temperature/molar conversions, ranges, speeds, +parser, clamping and suffixes. No numerical default, worker, recording, archive, +dependency or model code changes. The shared quantity helper also serves main +domain/maximum-step/stiffness and emitter fields; their caller sources are +unchanged, but presentation reach is broader than material fields. + +Two implementation hazards identified during review are resolved: + +- Full-precision density text initially pushed the following unit selector + outside the narrow form. An explicit next-row decision when remaining width + is insufficient preserves complete unit labels. `Ui::end_row` changes layout + without consuming an automatic widget ID. +- Ordinary pointer click originally selected the compact caption's length, + leaving a suffix when the edit buffer became longer. The unchanged thermal + click-to-replace test caught a real editing regression. After widget addition, + only `clicked() && gained_focus()` resets the existing TextEdit selection to + the full upcoming buffer length. Later caret clicks remain untouched. The + pre-widget scalar copy supplies that transient cursor length; it is not stored + as another canonical value or dirty-field owner. Tests retain ordinary typing + without forced Ctrl+A, rather than bypassing the regression. + +The adapter relies on the pinned egui ID/focus behavior; retain these regressions +when upgrading that dependency. + +## Inspected execution boundary and evidence + +Inspected all five allowlisted tests and their complete application helper paths: +local Context/frame/text/pointer/key/click/numeric-label helpers, material/domain +forms, quantity, and pure material/configuration defaults. They contain no +simulation, Worker, archive, process, filesystem-output or cleanup path. Numeric +label lookup identifies the isolated density editor or unique g/mol suffix, +without assuming a particular compact spelling or normalizing canonical values. + +The lead enumerated 168 names using list-only, then ran exactly one fully +qualified allowlisted name per invocation. The allowlist SHA256 is +`bb8aebb4607281551cfcfcb2c88eb206b8b384b1a39be4c766933de00d7a60d8`. +Reviewed final `/tmp/rheon-ui-precision-final-v3-exact-{1..5}.log`: each records +one passing test. These cover: + +- Exact material bits/configuration across pointer focus/blur and 28 forward + plus 28 reverse Tabs, Air and precise liquid properties including tiny + viscosity, all three density units, and 240/280 logical-pixel widths. +- Ordinary click-to-type replacement, explicit density and molar SI edits, + invalid text, upper/lower clamping and actual ArrowUp adjustment. +- Existing thermal edit/reopen, preset/fill/unit ownership and open-menu width + regressions through the same inert helpers. + +The lead's final strict Clippy/affected locked check/format logs pass; reviewer +`git diff --check` also passes. The reviewer did not execute tests or broaden +selection. The original material-bit failure, intermediate width failure and +pointer-edit failure remain in separate before/after/v2 logs; final v3 logs do +not overwrite them. + +## Preservation and remaining limits + +The original M2a settings source still hashes to +`0937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102` +in `/workspace/Rheon-ui-hierarchy`. The preserved packet/patch and earlier +test-selection incident retain their historical scope. Earlier automatic +successful-test teardown removed disposable numerical outputs before detection; +those bytes/resource measurements cannot be reconstructed or counted as retained. +No repeat, cleanup or reclassification follows from this precision review. + +UI-5 now has source/headless regression evidence for the inspected forms and +finite fixtures. Full application focus order, all shared-helper caller contexts, +native rendering/scales, representative usability and the word-count target +remain unverified. Live cancellation remains parked. There is no commit, +publication, kernel-resource provisioning or execution admission in this review. diff --git a/src/bin/desktop/settings.rs b/src/bin/desktop/settings.rs index d18d1904..e7eecfbc 100644 --- a/src/bin/desktop/settings.rs +++ b/src/bin/desktop/settings.rs @@ -26,6 +26,37 @@ fn preset_text(ui: &egui::Ui, text: impl Into) -> egui::Widget .into() } +/// Egui reparses cached edit text on blur. Only focused text needs lossless +/// formatting; compact idle formatting must never round the underlying value. +fn precise_drag(ui: &mut egui::Ui, value: f64, control: egui::DragValue<'_>) -> egui::Response { + let id = ui.next_auto_id(); + let ctx = ui.ctx().clone(); + let formatter = ui.style().number_formatter.clone(); + let response = ui.add(control.custom_formatter(move |value, decimals| { + // Query when invoked, after DragValue registers keyboard focus. + if ctx.memory(|memory| memory.has_focus(id)) { + // Debug keeps round-trip precision and uses scientific notation for + // tiny values, avoiding hundreds of leading zeroes in the editor. + format!("{value:?}") + } else { + formatter.format(value, decimals) + } + })); + if response.clicked() && response.gained_focus() { + // The button selected its compact text; the edit buffer will be longer. + // Align this initial selection only, preserving later caret placement. + let mut state = egui::TextEdit::load_state(ui.ctx(), id).unwrap_or_default(); + state + .cursor + .set_char_range(Some(egui::text::CCursorRange::two( + egui::text::CCursor::new(0), + egui::text::CCursor::new(format!("{value:?}").chars().count()), + ))); + state.store(ui.ctx(), id); + } + response +} + #[derive(Clone, Copy, PartialEq, Eq)] enum DensityUnit { KilogramsPerCubicMetre, @@ -111,17 +142,23 @@ impl MaterialControls { ui.horizontal_wrapped(|ui| { ui.label("Density").on_hover_text(DENSITY_HELP); let mut density = m.density / self.unit.factor(); - if ui - .add( - egui::DragValue::new(&mut density) - .speed(0.001) - .range(1e-9 / self.unit.factor()..=1e9 / self.unit.factor()) - .max_decimals(8), - ) - .changed() + if precise_drag( + ui, + density, + egui::DragValue::new(&mut density) + .speed(0.001) + .range(1e-9 / self.unit.factor()..=1e9 / self.unit.factor()) + .max_decimals(8), + ) + .changed() { m.density = density * self.unit.factor(); } + // The focused value can be wider; ComboBox's inner horizontal UI + // cannot wrap itself onto the next row. + if ui.available_size_before_wrap().x < 80.0 + 2.0 * ui.spacing().button_padding.x { + ui.end_row(); + } egui::ComboBox::from_id_salt("density-unit") .selected_text(self.unit.label()) .width(80.0) @@ -324,7 +361,9 @@ pub fn quantity( ) -> egui::Response { ui.horizontal_wrapped(|ui| { ui.label(label); - ui.add( + precise_drag( + ui, + *value, egui::DragValue::new(value) .speed(speed) .range(range) @@ -447,6 +486,309 @@ mod tests { } } + // UI-only helpers below use Context/MaterialControls and their pure config + // defaults. They never construct a simulation, worker, recording or file. + fn material_bits(controls: &MaterialControls) -> [u64; 6] { + let m = controls.config.material; + [ + m.density, + m.viscosity, + m.surface_tension, + m.wall_drag, + m.temperature_kelvin, + m.molar_mass, + ] + .map(f64::to_bits) + } + // Density is the only unitless numeric editor in this form; molar mass + // has a unique suffix. Do not assume the compact formatter's spelling. + fn numeric_label(output: &egui::FullOutput, suffix: &str) -> String { + output + .shapes + .iter() + .find_map(|s| match &s.shape { + egui::Shape::Text(t) + if t.galley + .text() + .strip_suffix(suffix) + .and_then(|s| s.parse::().ok()) + .is_some() => + { + Some(t.galley.text().to_owned()) + } + _ => None, + }) + .unwrap_or_else(|| panic!("missing numeric editor with suffix {suffix}")) + } + fn replace_focused( + ctx: &egui::Context, + controls: &mut MaterialControls, + side: &mut f64, + text: &str, + ) { + let _ = frame( + ctx, + 280.0, + controls, + side, + false, + key( + egui::Key::A, + egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }, + ), + ); + let _ = frame( + ctx, + 280.0, + controls, + side, + false, + vec![egui::Event::Text(text.into())], + ); + let _ = frame( + ctx, + 280.0, + controls, + side, + false, + key(egui::Key::Enter, Default::default()), + ); + let _ = frame(ctx, 280.0, controls, side, false, vec![]); + } + + #[test] + fn ui_precision_tab_and_focus_preserve_material_bits() { + for width in [240.0, 280.0] { + for unit in [ + DensityUnit::KilogramsPerCubicMetre, + DensityUnit::GramsPerMillilitre, + DensityUnit::MilligramsPerCubicMillimetre, + ] { + for preset in [FluidPreset::Air, FluidPreset::Water] { + let ctx = context(); + let mut controls = MaterialControls { + preset, + unit, + ..Default::default() + }; + controls.config.material = preset.material(); + if preset == FluidPreset::Water { + controls.config.material.viscosity = 1.2345678901234567e-200; + controls.config.material.surface_tension = 0.07281234567890123; + controls.config.material.wall_drag = 5.012345678901234; + controls.config.material.temperature_kelvin = 293.1512345678901; + } + let bits = material_bits(&controls); + let before = format!("{:?}", controls.config); + let mut side = 1.0; + let _ = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + let output = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + let density_label = numeric_label(&output, ""); + click( + &ctx, + width, + &mut controls, + &mut side, + false, + &output, + &density_label, + ); + let output = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + assert_eq!(material_bits(&controls), bits, "pointer focus without edit"); + let header = format!( + "Temperature · {:.2} °C", + controls.config.material.temperature_kelvin - 273.15 + ); + click( + &ctx, + width, + &mut controls, + &mut side, + false, + &output, + &header, + ); + let _ = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + let mut visited = std::collections::HashSet::new(); + for shift in [false, true] { + for _ in 0..28 { + let _ = frame( + &ctx, + width, + &mut controls, + &mut side, + false, + key( + egui::Key::Tab, + egui::Modifiers { + shift, + ..Default::default() + }, + ), + ); + let _ = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + if let Some(id) = ctx.memory(|m| m.focused()) { + visited.insert(id); + } + assert_eq!( + material_bits(&controls), + bits, + "focus-only change for {}", + preset.label() + ); + assert_eq!(format!("{:?}", controls.config), before); + } + } + assert!(visited.len() >= 10, "traverse actual form controls"); + let _ = frame( + &ctx, + width, + &mut controls, + &mut side, + false, + key(egui::Key::Escape, Default::default()), + ); + let _ = frame(&ctx, width, &mut controls, &mut side, false, vec![]); + assert_eq!(material_bits(&controls), bits); + } + } + } + } + + #[test] + fn ui_precision_explicit_edits_keep_si_units_and_ranges() { + for unit in [ + DensityUnit::KilogramsPerCubicMetre, + DensityUnit::GramsPerMillilitre, + DensityUnit::MilligramsPerCubicMillimetre, + ] { + let ctx = context(); + let mut controls = MaterialControls { + unit, + ..Default::default() + }; + let mut side = 1.0; + let mut expected = controls.config.material; + let _ = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + let initial = numeric_label(&output, ""); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &initial, + ); + // Ordinary click-to-replace must select the full precision text. + let _ = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + let _ = frame( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + vec![egui::Event::Text("2.5".into())], + ); + let _ = frame( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + key(egui::Key::Enter, Default::default()), + ); + let _ = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + expected.density = 2.5 * unit.factor(); + assert_eq!(controls.config.material, expected); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &numeric_label(&output, ""), + ); + replace_focused(&ctx, &mut controls, &mut side, "1e99"); + assert_eq!(controls.config.material.density, 1e9); + } + let ctx = context(); + let mut controls = MaterialControls { + preset: FluidPreset::Air, + ..Default::default() + }; + controls.config.material = FluidPreset::Air.material(); + let mut side = 1.0; + let _ = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &numeric_label(&output, " g/mol"), + ); + replace_focused(&ctx, &mut controls, &mut side, "32"); + assert_eq!(controls.config.material.molar_mass, 0.032); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &numeric_label(&output, " g/mol"), + ); + let _ = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + let _ = frame( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + key(egui::Key::ArrowUp, Default::default()), + ); + assert_eq!( + controls.config.material.molar_mass.to_bits(), + (32.1_f64 / 1000.0).to_bits() + ); + replace_focused(&ctx, &mut controls, &mut side, "32"); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &numeric_label(&output, " g/mol"), + ); + replace_focused(&ctx, &mut controls, &mut side, "not a number"); + assert_eq!(controls.config.material.molar_mass, 0.032); + let output = frame(&ctx, 280.0, &mut controls, &mut side, false, vec![]); + click( + &ctx, + 280.0, + &mut controls, + &mut side, + false, + &output, + &numeric_label(&output, " g/mol"), + ); + replace_focused(&ctx, &mut controls, &mut side, "-4"); + assert_eq!(controls.config.material.molar_mass, 0.0001); + } + #[test] fn ui_material_thermal_disclosures_preserve_values_and_si_editing() { for domain in [false, true] { From 7dcbfa67578875dd266020be589e2fbea40467f8 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 22:49:28 +0000 Subject: [PATCH 08/25] refactor(desktop): compact timeline and bound playback editing Measure only the disabled invisible timeline projection using ordinary layout, keep the measurement accessibility subtree hidden and commit real interaction once. Replace growing speed DragValue editing with a bounded labelled field, finite validation and keyboard adjustment. Four exact inspected UI-only tests cover cold/warm bounds, focus-only precision, invalid editor stress, native track selection and synthetic report captions; strict Clippy, affected check and formatting pass. Idle strip is48px instead of106px at620/970px available widths. Native, configured long-caption and popup keyboard coverage remain open. (cherry picked from commit cbe08067820ef65cf75f18504805ed2c020865d4) --- docs/plans/ui-refinement/execution-ledger.md | 61 +++ docs/plans/ui-refinement/plan.md | 429 +++++++---------- .../reports/backend-integration-receipt.md | 61 +++ .../reports/backend-integration-review.md | 116 +++++ .../reports/scene-authoring-admission.md | 75 +++ .../reports/scene-authoring-plan-review.md | 99 ++++ .../reports/scene-integration-contract.md | 206 ++++++++ .../reports/scene-plan-review.md | 169 +++++++ .../reports/timeline-compact-checkpoint.md | 120 +++++ .../reports/timeline-source-review.md | 265 +++++++++++ src/bin/desktop/timeline_ui_tests.rs | 443 ++++++++++++++++++ src/bin/rheon-desktop.rs | 360 +++++++++----- 12 files changed, 2018 insertions(+), 386 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/backend-integration-receipt.md create mode 100644 docs/plans/ui-refinement/reports/backend-integration-review.md create mode 100644 docs/plans/ui-refinement/reports/scene-authoring-admission.md create mode 100644 docs/plans/ui-refinement/reports/scene-authoring-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/scene-integration-contract.md create mode 100644 docs/plans/ui-refinement/reports/scene-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/timeline-compact-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/timeline-source-review.md create mode 100644 src/bin/desktop/timeline_ui_tests.rs diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index d5008041..a6f85768 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -239,3 +239,64 @@ - New scene/timeline direction supersedes continuing old M2–M4 unchanged. Freeze this precision evidence before admitting new source regions; independent scene contracts must separate UI ownership from the other task's force domain/adapters. + + +## 2026-10-20 — revised scene plan and compact timeline checkpoint + +- Superseding user direction admitted as T1 compact playback, T2 shared right + scene selection/properties, T3 canonical forces, T4 manipulators, T5 native/ + integration. Independent plan review, pinned structural validation and exact + ownership contract are recorded; no standards-engine certificate is available. +- Parent supplied shared backend API metadata at d7bffdd5 (f55cf595 authoring, + 19e0145c adapters). Exact source transfer is pending. Canonical nonzero + SceneObjectId allocation belongs to UI across models/forces; force semantics, + validation/freeze/adapters and qualified pressure/control custody belong to + backend. Exact runtime/archive file reservations require parent coordination. + No Request/dispatch/custody implementation changed; MAC stamp gap is explicit. +- T1 modifies only main's timeline presentation and its new inert exact test file. + The106px floor fails the <=70 idle target. Floor removal alone clips cold frames; + both logs retained. Local disabled/invisible sizing on disposable scalar copies + with disjoint IDs and hidden AccessKit subtree now yields47px at620/970px strip + widths. Two exact UI-only probes each pass; no prefix/fallback suite or workload. +- Source review limits acceptance: disabled DragValue measurement cannot see the + real focused editor's buffer. Speed-edit/track popup/report stress and general + layout safety are unaccepted. No local display; no new native/JPEG claim. M1, + genuine cancellation, scientific and full new scene acceptance remain open. +- UI-5 precision/settings is unchanged; original M2a/settings and incident log + hashes remain exact. Prior accidental fixture teardown removed disposable + outputs before detection; no resource measurements or scientific credit. + No commit, publication, remote overwrite, cleanup, cgroup/mount change or live + workload. See compact checkpoint for tests, native procedure and prerequisites. + +- Subsequent parent update confirms UI ownership of desktop Request/dispatch and + run_sets frozen-intent/load/rerun files, backend ownership of core recording. + Backend reports8d2b4f8a custody APIs and3e839cbf evidence commit; exact source + publication/transfer is pending verification. Five solver passes are pre-custody; + new custody has pure codec/static evidence only. No unsupported workflow enabled. + +- Published source now fetched at exact1d44c8429701f205f678b602040d4f62ea6457b3, + tree3a0f00235cd224bfae825bb3f09b4fa599bc46e9; core unchanged from3e839cb. + All13 custody seals match Git/local bytes; original35 backend paths and55 + unchanged desktop paths independently verified. Isolated branch integrates + verified M2a/UI-5/T1 prerequisites and backend source; no commit/merge/push. + Locked desktop check8.75s passed. No integration tests/workloads executed. + Scene inspector/picking/outline/handles are still plan/contract only, not built. + Source receipt and review qualify transfer, not desktop/runtime/native acceptance. + + +## 2026-10-20 — completed focused timeline source increment + +- Parent authorized continued implementation, distinct commits and normal draft-PR + publication after focused gates. Preserve all old work/evidence; no squashing. +- Bounded accessible playback-speed editor preserves exact focus-only bits, finite + parse and0.1–4 clamp, keyboard adjustment; invalid text retains last valid value. + New report-state test exposed sizing_pass alignment/Separator mismatch; ordinary + invisible/disabled local layout fixes it. Earlier failure log retained. +- Four individually selected --exact inspected UI-only tests pass; idle48px at + 620/970 strip widths, speed focus/Tab/edit/long invalid/clamp/keyboard, track and + synthetic report captions. No solver/model/history/Worker/file fixture/cleanup. + Clippy53.53s passed; compile/fmt/diff evidence recorded separately. Independent + source review has no bounded blocker; native/configured-popup scope stays open. +- Next admission covers canonical right scene list/properties/force creation, + immutable context selection and explicit launch refusal; viewport picking and + handles follow in separate bounded slices. No force runtime dispatch yet. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 1db70762..2b673e4b 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,315 +1,208 @@ -# Focused desktop UI refinement +# Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** UI-5 precision-only repair; original M2a worktree preserved -**Next slice:** UI-5 — exact-value focus regression, bounded editor adapter and review +**Current phase:** T1 source complete; T2 canonical scene authoring admission +**Next slice:** canonical right scene list and validated properties **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) **Issues:** [issues.md](issues.md) -[Standards/design](reports/standards-and-design.md) · [Plan review](reports/plan-review.md) · -[Implementation review](reports/implementation-review.md) · [Verification](reports/verification.md) · -[Native handoff](reports/native-validation-handoff.md) +[Scene integration contract](reports/scene-integration-contract.md) · +[Scene plan review](reports/scene-plan-review.md) · +[UI-5 precision repair](reports/ui5-precision-repair.md) · +[T1 compact checkpoint](reports/timeline-compact-checkpoint.md) · +[Backend receipt](reports/backend-integration-receipt.md) · +[Current authoring admission](reports/scene-authoring-admission.md) ## Objective -Reduce visible text and competing controls while making Rheon's actual native -application easier to use. The user accepted audit -`libfile_825ba1b07a3c8191a340bb761eea1c89`, version `0`, -`rheon-ui-audit-2026-10-20.txt`, then authorized a standards-compliant plan and -implementation. Canonical admission is this path, operation `start` after the -independent plan review; subsequent implementation uses `continue`. No optional -approval is required for the reversible first slice. - -Base remote main revalidated 2026-10-20: -`fd7c0f0aee63bc8710df99f9bb01154e7619b974`. Local branch -`implementation/ui-hierarchy-20261020`, worktree `/workspace/Rheon-ui-hierarchy`. -UI task owns implementation and serial integration; parent owns eventual main -integration. That branch is retained for review/validation. Parent later reports -its remote at `426fd25` after separate user action; do not overwrite it. Current -local continuation from `e109758` uses `implementation/ui-m1-cancel-contract-20261020`. -No push, PR, -merge, rebase, branch deletion or cleanup is authorized in this turn. +Follow the user's new direction: a vertically compact timeline and a collapsible +right-side scene tree/properties inspector. Forces and existing models are scene +objects with shared tree/viewport selection, a thin selected-object outline and +manipulator handles. Keep the accepted audit's clean hierarchy, concise text, +clear labels and progressive disclosure. Preserve units, errors, limitations, +immutable run inputs and existing archives. + +This direction supersedes continuing the former M2–M4 audit milestones unchanged. +The accepted audit remains libfile_825ba1b07a3c8191a340bb761eea1c89 v0; its40% word +reduction target is a matched-state secondary measure, never a reason to remove +needed object/force meaning or a substitute for this new feature's acceptance. +M1 remains incompletely accepted; old native evidence is scoped to426fd25 only. + +Canonical admission is this path, operation `continue`. Sole desktop UI owner: +this task. A separate task owns the shared force domain and both solver adapters. +Only independently owned UI work can begin before its exact API is reconciled. +No optional approval is needed for the reversible T1 slice after plan review. + +## Source, standards and preservation + +Local HEAD remains7318023f04946171d7909119300af76e455c2926. The original uncommitted +M2a worktree `/workspace/Rheon-ui-hierarchy`, its exact patch and logs are untouched. +Detached `/workspace/Rheon-ui-precision` applies that M2a overlay then UI-5; the +reviewed precision checkpoint is frozen as tree3e1fd57146e2f1a02abe22a09ac317497f39d738 +under `/workspace/scratch/rheon-ui-precision/checkpoint`. No commit/publication. +The original remote implementation branch is parent-reported426fd25; do not +fetch/reset/overwrite it or the separate Inspector/numerical branches. + +Continue the inspected standards route pinned to Coding-Standards0fc92a24279f3a4574c80af0a2f31ea347a21597: +Planning/Implementation/Verification-GUI/Proportionality, Rust, Accessibility, +Diagnostics, Architecture/Code Design and ownership/contracts. No standards-engine +MCP/certificate is available or claimed. Original standards/design and reviews +remain historical evidence; current source and this revised admission govern. ## Scope and non-goals -First: explicit preview versus Accepted Session setup, one primary launch at a -time, fixed run-action/status area, unchanged launch validation and worker -ownership. Later: collapse optional setup prose, shared control geometry and -accessible help, camera/playback consistency, D1 comparison/archive presentation. -Keep units, relevant limitations, blocking errors, absence/uncertainty, pressure -stage/time and integrity-versus-completeness distinctions accessible and explicit. +T1 removes wasted timeline height and isolates its pure painting/input boundary. +T2 adds the right scene list, one shared selection and canonical force authoring +properties. T3 adds viewport picking/outlines; T4 adds manipulators on those same +IDs. T5 wires qualified immutable runtime/archive paths and native evidence. -No numerical/tolerance/default-physics changes, solver/model/evaluator/corpus -execution, L1/MMS work, storage migration, new framework/dependency, web/Pages -redesign or wholesale D1 replacement. Do not modify README or local agent work. -No speculative integration of Inspector changes. +No competing force physics, backend-specific UI approximations, solver/model/ +scientific execution, unadmitted archive migration, worker/session ownership change, training, +L1/MMS work, dependency/framework rewrite, publication, PR/merge or cleanup. +Live cancellation stays parked on missing existing containment; no provisioning. +Canonical domain source is now received and sealed. No fabricated runtime support +while desktop dispatch/frozen intent/load/rerun integration remains pending. ## Dependency strategy and ownership -M1 is based on main alone and does not depend on local Inspector slice -`241dfb5237db0fa064d1257f4baf12781e0a0d44` or dependency -`1424f2d55323825c8907cf9209914f29130ec7de511`. Their separate validator task -`01a15a9f-ffcc-75ee-bd76-02f1c277f027` owns validation and its test-only Clippy -repair. Preserve its branches; do not copy/rebase/merge their code. Parent relays -coordination because cross-thread messaging is unavailable in this environment. -M4 must revalidate that work's final source before admitting Inspector edits. +See the binding [integration contract](reports/scene-integration-contract.md). +UI owns desktop composition/rendering/input/selection; UI allocates canonical SceneObjectId values across models/forces; force owner owns +the type, physical units/support/frame/time semantics, validation/freeze and both +solver adapters. The exact backend API/source at1d44c842 is received and sealed; desktop runtime +integration remains pending. See the source receipt and independent review. See exact proposed runtime/custody +file reservations in the contract; none is edited in T1. Force owner must not edit desktop UI files concurrently. UI must +not edit force/solver implementations. Exchange a pinned API/patch and serially +integrate only after source review. No speculative merges or duplicate physics. + +Recorded Inspector particle/cell probes retain their existing owner and identity. +They are not authoring-object selection. Reconcile the separately validated +Inspector slice before any overlapping changes; T1 excludes Inspector entirely. ## Milestones -| Milestone | Goal and exact production write set | Dependency / gate | State | +| Milestone | Exact production write set and outcome | Dependency / gate | State | | --- | --- | --- | --- | -| M1 | One selected setup workflow and persistent run controls. `src/bin/rheon-desktop.rs` (sidebar, presentation methods, UI state/tests only), `src/bin/desktop/configured_controls.rs` (form/action split and labels only), optional `src/bin/desktop/setup_ui_tests.rs` | Independent plan/design review; A1–A3 plus A6 supporting checks; A4 native evidence remains required | Verifying | -| M2 | Compact optional setup, aligned fields and keyboard-accessible help. `src/bin/desktop/{settings,collision_ui,emitter_controls}.rs`; optional new `src/bin/desktop/form_controls.rs` | M1 idle native feedback received; preserve policy and unit editing; A4–A5 | M2a source verified, native pending; remaining slices planned | -| M3 | Camera/transport consistency. `src/bin/desktop/{viewport,particle_view,engineering,transport_view}.rs`; optional `src/bin/desktop/playback_controls.rs` | M1/M2; separately re-admit exact main-file playback regions if needed; focus and session-ownership evidence | Planned | -| M4 | Promote existing D1 comparison/archive results. `src/bin/desktop/{comparison,comparison_field_view,archive_view,run_set_view,inspector}.rs` and `src/bin/desktop/ai_comparison/{mod,diagnostic_view}.rs` | M3 and final Inspector revision reconciled; re-admit exact composition-root regions; saved-fixture integrity/absence evidence | Planned | - -Production write sets are disjoint across planned milestones. A later need to -touch earlier integration regions is an explicit serial admission, never -parallel ownership. Only the UI lead writes this plan directory; reviewers own -non-authorizing review reports. Authored plan, ledger, issues and review reports -may change throughout. Generated logs/renders remain outside Git per -`docs/REPOSITORY_OUTPUTS.md`. +| T1 | main timeline presentation and desktop/timeline_ui_tests.rs; bounded labelled speed editor, ordinary invisible local measurement, unchanged physical context | Four inspected exact UI probes + compile/Clippy/fmt + independent source review; native remains separate | Completed (bounded source) | +| T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; no viewport/runtime changes | Active | +| T3 | viewport.rs picking and thin actual-geometry outline; scene_ui.rs same selection API; main projection routing | T2 source gates, reviewed exclusive pick/camera/probe policy; inert projection tests then native | Planned | +| T4 | desktop/scene_manipulator.rs, viewport gesture routing, scene_ui validated transform intent and collision_ui transactional model placement | Same IDs/domain capabilities; no duplicate force physics; Escape rollback, explicit model Apply | Planned | +| T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | + + +Stages execute serially under the UI owner; T2–T4 reuse some UI regions and are +not parallel write sets. Every next stage explicitly re-admits its exact regions +before editing. Force-domain write sets are disjoint throughout. Only the lead +writes canonical plan/ledger/issues; independent reviewers write their reports. +Generated logs/screenshots/transfer packets remain outside Git. ## Binding Decisions -1. `App` owns the setup selection; it does not change `material_mode` or any - numerical settings when switched. Preview retains material/carrier choice. - Accepted Session exposes its own physics form plus the existing shared - domain, grid, steps, maximum dt, pressure, preview, resource and CPU settings - that its existing launch consumes. Hide irrelevant emitter/mesh/material - controls for that workflow; do not hide consumed launch inputs. -2. Existing `start`, `start_configured`, validation, immutable intent publication, - `Worker`, `poll`, cancellation and child cleanup remain authoritative. UI - selection is disabled during active work. No new simulation loop or copied - worker state. Settings/Inspector switching must retain run control access. - Active work shows only frozen Request facts, not retained next-run form values. -3. Split configured form painting from its launch button so one fixed action - area above the scrolling form can return intent. Paint the current form and - process readiness invalidation before executing intent through existing launch - validation. Keep package inspection/readiness and - invalidation in `ConfiguredControls`; do not expose its private representation. -4. Preserve full status/error text in keyboard-reachable, height-capped scrolling - Status details; keep a concise single-line projection visible outside it so - long paths/errors cannot displace Cancel or the form. Shorten routine labels - and status copy only. - Existing string status is not parsed to invent severity/completion semantics. - Compact progress uses the existing worker's actual numerator/denominator; - cancellation remains the existing operation, not inferred success. -5. Keep advanced secondary actions available under task-named disclosures. - Preserve relevant limitations and clearing/retention consequences. No - unexplained icons or reduced body font/hit targets to meet text targets. -6. No browser renderer is substituted for actual native validation. The audit's - historical `a552d0a` capture is not a current baseline. +1. App owns one ephemeral typed scene-object selection and current scene context, including import/replacement generation for the current singular model slot. + Tree, viewport and properties emit Select/Edit intents against that owner; no + duplicated canonical force/model properties. Draft and frozen-run contexts are + explicit, and missing/deleted objects clear selection without retargeting. +2. Right scene panel collapses without losing selection/expanded rows/draft values. + Properties belong to the selected object, not a disconnected force form. + At narrow logical widths use one open setup/scene drawer at a time while an + App-owned global run/status strip keeps the one launch action/Cancel reachable. + Maintain usable viewport width; do not solve crowding by shrinking body fonts. +3. Force units, support, activation/time, coordinate frame, meaningful transforms, + density/mass conversions and backend support come only from the domain API. + Unsupported backend/object edits show blocking reasons. Do not map existing + MAC-only BodyForce to particles or confuse visual anchor with physical support. +4. Active, paused, completed and cancelled retained-run scene geometry/properties come from immutable Request/snapshot + facts and are read-only, independently of worker presence. An explicit Draft + scene switch stops displaying retained fluid/run geometry without deleting + history; selection clears across contexts. T2 must reconcile this before edits. Draft edits affect a future run through the existing + validate/freeze/launch boundary; no mutation of an active session/archive. +5. Existing collision draft path/scale/translation and accepted Arc remain + distinct. Keep transactional Apply/rejection and explicit history consequences. + Accepted geometry is never silently replaced by an unvalidated drag preview. +6. Handle capture has priority over object pick, recorded probe and camera input. + One gesture has one owner. Escape cancels preview, release validates/commits one + draft authoring edit (models still require explicit transactional Apply), and numeric unit-labelled properties provide a keyboard alternative. + A thin geometry-following selection outline accompanies tree/property identity; + colour alone does not communicate selection or errors. +7. T1 retains transport/speed/Live/track/scrub semantics and physical time/step + metadata. Replace the height floor with local content measurement; do not discard status/uncertainty or + move scientific details until a later measured need justifies that exact change. +8. Every test path is inspected before list-only enumeration; execute one explicit + fully qualified `--exact` UI-only name per command. No substring filters, + fallback suites, simulations, model runtimes, archive fixtures or file cleanup. ## Objective Acceptance | ID | Observable criterion | Kind / environment / mode | Status | Evidence | | --- | --- | --- | --- | --- | -| A1 | Actual egui pointer selection presents only the selected setup form and one primary launch; switching preserves settings and never launches work | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | -| A2 | Primary action/status remain outside scroll content, including Inspector mode; shared consumed inputs remain reachable; disabled package launch remains explicit | integration / simulated egui input / automated | satisfied | [Verification](reports/verification.md) | -| A3 | Launch/request/worker/cancel/recording semantics unchanged; no Inspector/D1/numerical edits; unit and limitation copy retained | focused / not-applicable / manual source review | satisfied | [Verification](reports/verification.md) | -| A4 | Native 1250×820 and 900×650 at 100% and 150% UI scale: no clipped labels/overlap; mode/action/status visible; scroll/keyboard/focus/help and in-progress cancellation work | user-workflow / required-real supported native display / manual | blocked | [Scoped native receipt and remaining gaps](reports/m1-native-and-gap-handoff.md); idle repaired UI passed per parent, active/native accessibility gaps remain | -| A5 | Matched current before/after states show materially less text with unchanged values/warnings; target 40% fewer visible explanatory words across full planned refinement, not M1 alone; 4/5 users identify workflow/edit material/find launch within 30s | user-workflow / representative native / manual | pending | No counts/usability results yet | -| A6 | Rust 1.92 formatting, affected desktop build, strict affected-target Clippy and focused UI tests pass | focused / Linux pinned Rust / automated | satisfied | [Verification](reports/verification.md) | - -Before expanding beyond M1, native owner observes real default/switched/Inspector -screens and overflow; that observation can revise panel placement/width, not -scientific semantics. Run ongoing-worker/cancel UI coverage only with an approved -UI-only fixture or independently authorized run; this turn authorizes no solver -or model execution. Headless paint establishes emitted layout/input behavior, -not human perception, native scaling, accessibility or complete workflows. +| S1 | Empty idle timeline fits <=70 logical px in900/1250px windows with the preceding280px sidebar reserved; selected-frame captions remain present and may naturally wrap/grow; actual Play/Pause/step/Live input keeps prior transitions | integration / isolated egui painting/input / automated | pending | Four exact probes pass: idle48px versus106px, focused speed editing and native-track/literal-report layouts. Native/configured/popup-keyboard coverage remains open | +| S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | No scene implementation yet | +| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Exact canonical source received/sealed; desktop dispatch/frozen intent integration pending | +| S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | Domain/manipulator stages not admitted yet | +| S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; prior426fd25 images do not cover this source | +| S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T1 focused gates reported in checkpoint; no scientific certificate | + +The former M1 A4/A5 and live cancellation remain unsatisfied. UI-5 has a reviewed +isolated-form exact-bit repair, not native/global keyboard acceptance. Earlier +accidental numerical-fixture results earn no acceptance credit; automatic test +teardown removed their disposable outputs before detection and no resource +measurements/scientific result are claimed. Preserve every incident/failure log. ## Simplicity And Ownership Review **Applicability:** `applicable` -The desktop composition root changes. - -- Independent concepts and dimensions: App selects displayed setup and routes intent; ConfiguredControls - owns package/form readiness; worker owns execution; recording owns durable - truth. Only presentation timing/placement changes for desktop users. -- State, identity, value, time, policy, and mechanism: required interleaving is selected intent plus actual worker busy state. - Avoid coupling UI selection to physics mode, timeline selection or archives. -- Caller and composition-root knowledge: App knows the selected workflow and asks ConfiguredControls to paint its - form/action; it does not know package pin details or invent execution status. -- Representative change paths and forced owners: new workflow label changes only presentation. New configured readiness rule - stays in ConfiguredControls. Cancellation still changes only worker owner. -- Stable Interfaces versus hidden knowledge: the form/action interface carries enabled state and action intent; it does - not leak provider layout, source pins or shutdown ordering. -- Independent evolution, testing, failure, and replacement: forms are paintable/testable without execution. Existing worker failures - remain full status feedback; a failed package check cannot enable launch. -- Deletion and cumulative machinery result: no permanent registry, adapter or generic UI framework is introduced. Removing - selection/placement would restore competing launch forms, so that mechanism - contains the user-facing hierarchy requirement. Ordinary functions suffice. -- Necessary complexity and containment: inherent complexity is preserving two existing run profiles and shared - inputs; contain it in App's form composition. No new persisted UI schema, - parallel state model or solver abstraction is retained. +- Independent concepts and dimensions: App composes scene context/selection and intent; UI paints + canonical projections; force domain validates/freezes physical meaning; solver + adapters execute it. Inspector owns recorded numerical probes separately. +- State, identity, value, time, policy, and mechanism: one selection keyed by stable + domain/object ID within draft/frozen context; no parallel force-value registry. +- Caller and composition-root knowledge: desktop knows capabilities and routes explicit + intents, not force integration formulas or backend density/mass policy. +- Representative change paths and forced owners: new domain force type supplies capabilities/properties; + new renderer changes drawing only; new model import retains geometry ownership. +- Stable Interfaces versus hidden knowledge: typed selection/edit intents and pinned validated freeze API, + not string-parsed status or copied provider/worker internals. +- Independent evolution, testing, failure, and replacement: T1 paints/tests without history access; force + validation failure retains draft/accepted values and a visible blocking reason. +- Deletion and cumulative machinery result: ordinary modules and enums suffice. No ECS, + scene framework, duplicate physics schema or universal widget abstraction. +- Necessary complexity and containment: draft-versus-frozen identity and exclusive gesture capture + are inherent; contain them at App/scene boundary rather than inside solvers. ## Rollback and re-plan -M1 is presentation-only, reversible by reverting its isolated local diff/commit; -no migration, archive deletion or saved-data rewrite is needed. Preserve branch -and worktree until parent accepts a terminal disposition. Rollback must not -reset another worktree or alter the Inspector/numerical branches. +Preserve all checkpoints. Roll back only a slice's exact incremental working patch, +never reset the original M2a/Inspector/numerical worktree or delete saved output. +T1 is presentation-only; no migration. T2–T4 require explicit source/identity +review before rollback because scene selection may refer to removed draft objects. +No persistent-data rewrite is admitted. ## Re-Plan Triggers -Re-plan for missing consumed launch inputs, changed package/worker ownership, -native evidence contradicting placement, overlap with Inspector integration, -unintended numerical changes or an inadequate UI-test oracle. Fix ordinary -lint/test failures inside the admitted boundary without restarting design. +Re-plan for force API/capability mismatch, UI/force overlapping ownership, silent +backend approximations, broken active/draft identity, native drawer crowding, +changed collision admission, lost units/errors, or any uncertain test helper path. +Ordinary lint/layout failures stay within the current exact slice and retain logs. ## Blockers -Local native display remains unavailable. The existing validator has reported -scoped idle native success at exact `426fd25`; active-worker, native long-status, -accessibility and representative user evidence remain incomplete. JPEG delivery -is blocked separately (Forbidden; no confirmed Library IDs). No standards application MCP is exposed; direct -source standards review is used and no engine conformance certificate claimed. -Build/tool availability and any baseline lint failures are recorded in the ledger. +Canonical force source is received/sealed in isolated branch; its arrival does not +implement scene UI or desktop runtime. Desktop dispatch/frozen intent/load/rerun +and unsupported owner paths still need staged integration and qualified evidence. +Right-scene +integration must reconcile exact Inspector work before overlap. Local native +display unavailable; JPEG delivery of older screenshots remains separately blocked. +Live cancellation lacks required existing containment and stays parked. No mount, +cgroup, permission, credential or network changes are authorized. ## Final Acceptance -Scoped idle native visual/interaction evidence is recorded; complete native -active-worker/accessibility and matched-state usability acceptance remain unsatisfied. -No delivery, standards-engine certification or whole-plan acceptance is claimed. - -## M1 native-feedback repair admission — 2026-10-20 - -Parent reports controlled native validation of checkpoint `8bf5f41` found expanded -form text clipping and viewport information clipping at 900×650 / 150%. Only -width wrapping is admitted: the existing M1 form files/tests plus the exact -`settings.rs` form rows and `viewport.rs` appearance captions. This is a serial -repair admission, not the start of M2 or M3; their remaining work stays Planned. -Preserve strings, units, warnings, fonts, values, interaction order and worker -ownership. Keep recording disclosure identity when supplying a wrapped galley. - -Accept the repair only after focused UI regressions and compile/lint checks, -independent source review, then exact-revision native inspection of expanded -Preview/Accepted Session forms and appearance captions at 1250×820 and 900×650, -100% / 150%. Recheck independent scroll and input retention; capture actual -updated JPEGs at quality 85 with revision/state labels. No workload is needed. -Active progress/Cancel/frozen rerun and long-status stress remain unverified; -exercise only under the previously bounded fixture/run authority. Parent reports -30 retained native JPEGs of the prerequisite; no images or native verification -of this repair are claimed here. Roll back only the isolated repair commit if -needed; preserve the accepted prerequisite and unrelated Inspector work. - -## Scoped native checkpoint and remaining M1 admission — 2026-10-20 - -Parent reports exact `426fd25d22975e1259f5543c77dc771a6ac646ff` and both -incremental transfer/parent/tree hashes validated in the existing native owner. -The repaired idle workflows passed at both supported sizes/scales. This closes -those clipping observations only; A4 remains blocked on its unobserved parts and -A5 remains pending. See [the scoped receipt/gap handoff](reports/m1-native-and-gap-handoff.md). - -Continue M1 with test-only synthetic active-state coverage in -`src/bin/desktop/setup_ui_tests.rs`; no production changes or new runtime fixture -flags. Reuse the existing inert Worker-handle pattern, empty budgeted history and -manual progress atoms. Verify actual sidebar Cancel routing/frozen-request -projection without `Worker::start`, solver calls or recording publication. This -is mocked UI-state evidence, not worker cancellation or native verification. -Propose precise bounds for any actual worker test; do not execute it under this -admission. No M2–M4 expansion, changed resource budget, model/scientific run, -publication, cleanup or retry of Forbidden image delivery is admitted. - -## M1 cancellation-contract admission — 2026-10-20 - -Parent requests a complete external supervised cancellation contract, not live -execution. Serial write set: `src/bin/desktop/setup_ui_tests.rs` (ignored genuine -case plus inert long-status stress), new `tools/supervise_ui_cancel.py` and its -unit tests, plus this plan directory. No application production code changes. -See [reviewed cancellation contract](reports/m1-cancellation-contract.md). - -Keep genuine execution ignored/unadmitted. Require exact source/binary seals, -externally reserved preconfigured cgroup and bounded output tmpfs; fail before GO -if prerequisites are unavailable. No fallback/provisioning/settings changes. -The hard resource/disposition contract must distinguish CPU bandwidth, per-process -limits, observed aggregate trips, kernel allocated storage, and unresolved kernel -teardown. Only disposable owned test processes/descendants can be terminated. -Independent source review and non-numerical tests precede any future admission. -Long-status headless stress can proceed now; native accessibility, genuine worker -cancellation and whole M1 acceptance remain open. M2–M4 stay Planned. - -## Read-only containment preflight and M2a admission — 2026-10-20 - -Parent authorizes proceeding with the next independent UI milestone while parking -only live cancellation. Read-only preflight confirms cgroup2 is mounted read-only; -the only visible child (`init`) is populated and not writable. No exclusively -delegated empty writable leaf exists. Existing `/tmp` and `/dev/shm` tmpfs mounts -have capacity 9,441,337,344 bytes and 2,305,014 inodes, no `noswap` option or -exclusive lease, exceeding the reviewed 32 MiB/1,024-inode contract. No mounts, -delegation, limits, permissions or process membership changed; no genuine test -ran. The reviewed supervisor remains unchanged. See -[preflight and M2a evidence](reports/m2a-material-domain.md). - -Serial M2a production write set: **only** `src/bin/desktop/settings.rs`, including -its UI-only tests. Authored plan/review/evidence files may change. Base is local -`7318023f04946171d7909119300af76e455c2926`; preserve every existing commit and -remote branch, with no publication. M1 is not accepted. No Inspector, D1, -collision/emitter implementation, main-file launch routing, worker, solver, -recording or numerical default changes are admitted. - -Keep material state/density/viscosity/surface tension/wall drag, gas molar mass and -reference pressure directly visible. Group temperature editing and thermal -explanations under explicit default-closed disclosures, whose headers retain the -current temperature in °C. Keep phase/no-boiling/no-freezing/no-elastic-slime -limitations outside disclosures. Add a named keyboard-reachable Property help -disclosure containing the existing hover explanations, preserving their meaning. -Bound all material/domain preset selector minimum widths and wrap full selected -labels without truncation. Preserve widget IDs for existing selectors, units, -ranges, conversion, defaults and SI owners; hiding/reopening inputs must not -change values. Do not alter the shared quantity helper in this slice. - -Acceptance evidence: real egui pointer/keyboard input opens and closes both -thermal disclosures and Property help; disclosure operation/idle painting retains -all configuration values; values edited through reopened controls preserve SI -conversion; all preset/fill choices fit 240/280 px form widths; essential model -limitations and gas reference pressure remain emitted when thermal help is closed. -Run focused material and existing setup UI tests, formatting, affected locked -compile and strict Clippy; obtain independent source/design review. These tests -are headless and cannot accept native appearance, accessibility or the 40% word -target. Native handoff requires idle material/domain screens, expanded thermal/help -states and dropdowns at both supported window sizes/scales, exact source seal and -quality-85 actual JPEGs; no run/cancel workload is needed. Rollback is the isolated -M2a working diff; no migration or saved-data operation is involved. - -Known M2a evidence limit: existing forward-Tab numerical-editor rounding is open -as UI-5, with exact Air SI values/reproduction in the report. The help test proves -only its isolated reverse-Tab route; no general keyboard value-immutability or -accessibility result is claimed. An accidentally broad `ui_` filter executed five -existing numerical-fixture tests before detection; its full names/effects and log -are recorded in the report. It is not admitted workload or UI acceptance evidence. -Subsequent runtime checks use only the inspected `settings::tests::` and -`setup_ui_tests::ui_setup_` prefixes. The genuine cancellation test remains unrun. - -## UI-5 precision-only continuation — 2026-10-20 - -Parent reviewed the test-selection incident and admits only the smallest focus -precision repair. Preserve the original uncommitted M2a worktree and packet: -new detached worktree `/workspace/Rheon-ui-precision` starts at7318023 and applies -the exact preserved M2a patch0c8b8d4e4eb87f761ff94f998f58548888ba23aa6b40414605652044816c2a6b, -whose resulting tree is8740bf155b3db1f3c44e109277605b9411f459fe. No commit or -publication is admitted. Production scope is only settings.rs: one private -DragValue formatting adapter at the density editor and shared quantity helper. -Focused text must roundtrip the exact display value; idle compact formatting, -IDs, parser, ranges, speeds and `.changed()` SI conversion guards stay unchanged. -Shared quantity callers include domain/main/emitter controls; record that reach. - -UI-5 was observed after M2a, not on a tested pristine baseline. The same pinned -format/reparse mechanism in prerequisite source is an inference about prior -cause, not an empirical baseline result. Repair evidence must retain exact-bit -material equality across focus/forward and reverse Tab without typing, and real -edits/range enforcement in SI units. Check focused240/280px geometry, not only -idle labels. No native accessibility/visual acceptance follows from these tests. - -Before runtime: inspect each entire test/helper path, enumerate with list-only, -then execute only an individually named `--exact` UI-only test. No substring or -fallback suite, simulation/model/numerical fixture, file cleanup or genuine -cancellation. Stop if a selection is uncertain. Independent source review is -required. Keep failure logs and original M2a/incident artifacts intact. Automatic -successful-test teardown removed the accidental disposable numerical outputs -before detection; no resource measurements or scientific acceptance are claimed. +T1 focused speed edit-buffer geometry and native carrier track/literal-report probes +pass after bounded editor and ordinary-layout measurement repairs. Configured +long captions, popup keyboard/window bounds and native behavior remain unaccepted. +No complete UI, force parity, native/accessibility, M1 or scientific acceptance is +claimed. Accept only exact staged source/test/native evidence once its objective +criteria are met. Publish/merge only after separate coordination/authorization. diff --git a/docs/plans/ui-refinement/reports/backend-integration-receipt.md b/docs/plans/ui-refinement/reports/backend-integration-receipt.md new file mode 100644 index 00000000..199862f4 --- /dev/null +++ b/docs/plans/ui-refinement/reports/backend-integration-receipt.md @@ -0,0 +1,61 @@ +# Canonical backend source receipt + +2026-10-20. Desktop integration branch: +implementation/ui-scene-integration-20261020 in /workspace/Rheon-ui-scene. +HEAD remains7318023f04946171d7909119300af76e455c2926; no new commit/merge/push. +Original M2a, precision and timeline worktrees/checkpoints remain preserved. + +The published backend branch was fetched by its explicit ref, expected tip +1d44c8429701f205f678b602040d4f62ea6457b3, tree +3a0f00235cd224bfae825bb3f09b4fa599bc46e9. Its core custody is byte-identical to +3e839cbf7c6aca56e8297ef740f7e48543de77b1, tree +c5e1f16a45a2244405610335dfeffdbc0123eca6. The tip adds only the prepared recording +integration test and plan, neither executed here. All13 custody-source seal entries +match both fetched Git objects and integrated local bytes. Backend's35 original +changed paths are exact. An independent reviewer checked those paths, all1372 +entries of the earlier combined tree and all55 desktop files unchanged by transfer. + +Integration sequence used fresh isolated branch at7318023, then exact preserved +M2a patch ->tree8740bf155b3db1f3c44e109277605b9411f459fe, +UI-5 patch ->tree3e1fd57146e2f1a02abe22a09ac317497f39d738, +T1 patch ->tree90bf811a5213d8fa021e9ff4323345f669fb1284, +then the pinned backend diff ->tree31033fb2c040c391603ed83ef31830958188c876 +(before receipt/review documents and the final two prepared-test/doc files). +Every prerequisite tree and patch application was mechanically verified. +No cherry-pick/merge, speculative rebase or unrelated worktree mutation. + +Locked offline desktop check after core integration passed in8.75s: +/tmp/rheon-ui-scene-backend-check.log. This is compilation only. No backend, +recording, numerical or model test was executed here. Upstream five adapter tests +apply only to its PRE-CUSTODY binary; current custody has pure codec/static +coverage. Do not claim post-custody runtime/SQLite/desktop acceptance. + +The initial text-form backend patch produced EOF-whitespace notices for preserved +raw evidence logs during apply. Files remain byte-identical to fetched source; +the backend .gitattributes marks those sealed logs as binary. Nothing was trimmed +or cleaned. Current whitespace check must respect those attributes. + +## Current UI progress and next boundary + +UI-5 precision: five exact inspected UI-only probes passed before T1; settings +SHA099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121 unchanged. +T1 compact timeline:47px versus106px empty idle; two exact UI-only probes and +check/Clippy/fmt pass. Focused speed buffer/track/report/native acceptance stays +open. Main SHA c0f7b12a2b75b95bb4c70da920b343da36658fc6c766b4f156bf51af88e57695 +and timeline tests12bee2a70ab3b6cb40a73e183e0786d220ed516761c416bafa7ca097c64352fa +remain exact through backend integration. + +The right scene inspector is contract/planning work only: no panel, shared picking, +outline, handles or force authoring UI is implemented yet. Domain source arrival +removes that source dependency. Next admit a bounded T2 write set under the existing +sole UI owner; do not claim backend arrival implements the user's workflow. + +Parent confirms UI ownership of main Request/launch/run_material, +desktop/carrier_execution.rs dispatch, desktop/run_sets.rs and +run_sets/{configured.rs,source.rs,configured/archive.rs} frozen-intent/load/rerun. +Backend owns core recording/solver custody. No runtime/request/desktop archive +integration changes yet. Configure/session/provider/free-surface force paths +remain unsupported and must visibly refuse rather than silently approximate. +Preserve recorded identity/provenance and use immutable run snapshots for glyphs. +Native scene/JPEG validation remains unavailable locally; no run/cancel workload +is part of the idle visual handoff. diff --git a/docs/plans/ui-refinement/reports/backend-integration-review.md b/docs/plans/ui-refinement/reports/backend-integration-review.md new file mode 100644 index 00000000..ae795a6c --- /dev/null +++ b/docs/plans/ui-refinement/reports/backend-integration-review.md @@ -0,0 +1,116 @@ +# Independent backend transfer/integration review + +2026-10-20. Read-only source review of the force backend overlay in +`/workspace/Rheon-ui-scene`, branch `implementation/ui-scene-integration-20261020`, +HEAD `7318023f04946171d7909119300af76e455c2926`. No tests, numerical/model workloads, +native UI, SQLite operations, compilation, publication or Git mutations were +executed by this reviewer. Only this review report was authored. Repository and +workspace `AGENTS.md`/local skill instructions were sought; none were present at +the inspected paths. + +## Disposition + +No blocking source-transfer or integration-disjointness finding. The canonical +backend is present unchanged, with its API available to the desktop feature. +This admits subsequent independently reviewed UI authoring work against those +types; it does **not** accept desktop force dispatch, frozen-intent migration, +recording integration, solver parity or scientific behavior. None of those +desktop paths is implemented by this overlay. + +## Exact identities and independently checked seals + +| Item | Verified identity | +| --- | --- | +| Supplied backend revision | `3e839cbf7c6aca56e8297ef740f7e48543de77b1` | +| Backend committed tree | `c5e1f16a45a2244405610335dfeffdbc0123eca6` | +| Integrated pre-new-UI checkpoint tree | `31033fb2c040c391603ed83ef31830958188c876` | +| Custody source manifest SHA-256 | `d873dd98d5d59d1f1aac2221cb5f1f30ae574bd6dff91c95d95d82a0c927a9ca` | +| Preserved desktop main SHA-256 | `c0f7b12a2b75b95bb4c70da920b343da36658fc6c766b4f156bf51af88e57695` | +| Preserved UI5 settings SHA-256 | `099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121` | +| Preserved T1 tests SHA-256 | `12bee2a70ab3b6cb40a73e183e0786d220ed516761c416bafa7ca097c64352fa` | + +The 13 entries in `force-authoring/evidence/custody-source.sha256` were hashed +directly from the supplied revision's Git blobs and compared to the manifest; +each working file also equals its committed blob. The manifest itself equals +the committed manifest. All 35 paths changed between the backend's `fd7c0f0` +base and supplied revision equal their supplied Git objects locally, including +the force documentation and evidence. All 1,372 entries in the integrated +checkpoint tree equal their current local file bytes. These checks read files +and objects; they did not stage untracked files or rewrite the tree. + +The manifest is a source identity receipt, not an authentication or execution +certificate. In particular, importing the backend's whole branch relative to +UI HEAD would have reverted unrelated UI changes because its base predates +them. The actual checked overlay avoids that problem: the backend change set +contains no `src/bin` paths; all 55 current desktop files equal the preserved +UI5+T1 worktree. `Cargo.lock`, session/material-session/provider owners and both +cancellation-supervisor files also remain byte-identical to UI HEAD. Inspector, +desktop run-set custody and launch dispatch were not replaced by this transfer. + +## Canonical API and guard inspection + +`src/lib.rs` exports the single canonical `SceneObjectId`, `ForceScene`, +`ForceObject`, transforms, kinds, support, time, snapshot and validation error; +`ForceUnits` remains the shared type from `forces.rs`. `Cargo.toml` adds only +the optional `force-serde` feature. Authoring derives are enabled by either +that feature or `desktop`; desktop already enables the Serde dependencies. +Recording remains native-only under its existing target gate. There is no +parallel desktop force-domain implementation or premature desktop call site. + +`force_field.rs` validates version, object/name bounds, nonzero and force-list +unique IDs, finite properties, quaternion tolerance, support and activation +before deep-copying a scene into the private immutable `Arc`-backed snapshot. +The snapshot exposes a shared scene reference and no mutable/Serde bypass. +Cross-model/force uniqueness and allocation remain UI responsibilities; the +backend deliberately validates only its force subset. JSON deserialization is +not validation, and a future UI importer must bound its input before parsing. + +The transferred public accepted-pressure and particle-control stamps retain +actual frozen fields. The inspected MAC authored path assigns its force stamp +after commit; candidate preparation/reset invalidates pressure stamps. Particle +authored controls are replaced on success, retained on rejection, and cleared +by a successful legacy step. This source inspection does not establish runtime +acceptance of these paths on the integrated tree. + +`recording.rs` re-exports the bounded codec, run binding/association checks and +carrier/material publication wrappers. `force_fields.rs` checks the 256 KiB +input bound before decode, validates schema and objects, and rejects trailing +bytes. It binds the complete scene digest through a versioned configuration +suffix. `require_fields` permits attaching metadata at construction, but an +accepted publication must already contain matching actual custody; it cannot +simply bless the caller's current draft. Writer association validation precedes +the transaction, and reader validation follows the existing publication digest +check. The material standalone decoder's absent run association is documented; +use the reader or explicit association check when a run header is available. + +Legacy SQL version and digest implementation are unchanged in the inspected +diff. This is source preservation plus upstream fixture evidence, not a claim +that arbitrary historical archives have been exercised. Missing metadata means +legacy/absent declaration, not proven zero external force. + +## Evidence boundaries and next admission + +The supplied check log `/tmp/rheon-ui-scene-backend-check.log` reports a successful +locked desktop check in 8.75 seconds. It was read, not rerun. Upstream documents +report seven pure codec tests and static checks after custody changes. Its five +bounded adapter runtime results belong exclusively to pre-custody binary +`0042e68eb0de3f63e3d4ef98506a88f417a4f965f63c41b64890f4ee9e9003cd`. +The preserved pre-custody seal differs from this custody manifest, including +force-field, simulation and particle source hashes. Those runtime results +cannot be promoted to the newer custody source or this desktop integration. + +No post-custody solver, SQLite, desktop dispatch, native interaction or archive +rerun was executed for this review. Existing T1 focused-editor/track/report and +native/accessibility gaps remain open; the source transfer does not resolve them. +Core recording ownership remains with backend; Request/freeze/launch, +carrier/material dispatch and versioned desktop run-set intent remain with UI. +Nonempty scenes must be visibly refused on a workflow until its exact owner +path and custody are qualified; session/configured/provider/transport support +must not be inferred from direct adapter exports. + +The copied scene contract/plan still includes historical statements that exact +source transfer is pending and the baseline timeline has a 106px floor. This +receipt supersedes transfer-pending claims only. Canonical progress records +should distinguish verified source arrival from still-pending runtime custody +and retain the T1 partial acceptance boundary. No production repair is required +by this integration review. diff --git a/docs/plans/ui-refinement/reports/scene-authoring-admission.md b/docs/plans/ui-refinement/reports/scene-authoring-admission.md new file mode 100644 index 00000000..61a2abd2 --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-authoring-admission.md @@ -0,0 +1,75 @@ +# Next bounded slice: canonical scene authoring + +Owner: desktop UI task, isolated implementation/ui-scene-integration-20261020. +Precede this slice with distinct preservation/precision/timeline/backend commits; +no squash of original M2a or imported backend evidence. No numerical/model tests. + +## Write set + +- New src/bin/desktop/scene_ui.rs: one canonical editable ForceScene, checked + SceneObjectId allocator shared with the one accepted model, one scoped selection, + Draft/Retained-run context, right scene list and selected properties. +- src/bin/rheon-desktop.rs: App owns SceneUi; compose its right panel; synchronize + transactional model replacement/removal (restore previous accepted mesh/ID and + preserve history if fresh ID admission fails; include initial CLI admission) and successful run-context changes; + select draft versus immutable retained geometry consistently; refuse new draft + launches containing any force object before path/recording integration. +- Exact UI-only tests in scene_ui.rs or desktop/scene_ui_tests.rs. No viewport, + solver/core recording/run_sets changes in this first authoring slice. + +## Behavior and identities + +Add force menu creates/selects Directional, Radial or Vortex canonical objects. +Use supplied IDs, units, metres/quaternion transforms, support/falloff and [start,end) +seconds. No second force schema/evaluator. Properties edit the canonical authoring +scene, report backend freeze errors, and never mutate a retained run. Invalid draft +is visible and cannot launch. Existing gravity stays additive; communicate that +without silently changing it. Default new strengths are zero to avoid unintended +extra gravity. Version1 excludes parenting/scale/animated transforms. + +Selection is App-owned SceneObjectId scoped by Draft versus Retained-run context; +future viewport picks/handles call this same selection owner. Tree collapse keeps +selection. Remove/replacement clears the old selected model/force. IDs use checked +nonzero monotonic allocation across model+forces; exhaustion refuses, never wraps. +Only one accepted collision model exists. Its first properties are read-only +accepted geometry; existing transactional import/placement remains in setup. +Label the collection Authored forces; existing gravity/configured-only forms +remain separate real physics, not proof of all/no forces. No save/load interface is added here; its later admission must enforce the +whole-scene imported-ID collision/exhaustion policy already in the contract. + +A retained run reads actual immutable Request geometry. Its old force metadata is +unavailable, not reconstructed from the current draft or declared absent physics. +Switching context clears selection and stops hidden playback, preserves retained +history/index, and never overlays draft geometry onto historical fluid. New forces +are disabled while retained context is selected or an active worker owns inputs. + +At narrow width (<1100 logical px) opening Scene replaces the left setup drawer +with a top run/status/Cancel strip using the same existing owner/actions. Closing +Scene restores setup values. At wider sizes both side panels are available. +The right panel's collapse control stays named and keyboard accessible; its form +scrolls independently. Scientific properties have unit-labelled controls and +optional details. Essential validation/error/unsupported status stays visible. + +## Runtime gate and next slices + +Any nonempty draft document, including disabled objects, visibly refuses new +simulation/configured launch before output/Worker creation. Legacy frozen reruns +retain their actual recorded intent; they do not consume the current draft. +No authored Request snapshot or force-aware dispatch is claimed in this slice. + +Next bounded slices wire actual viewport picking/thin geometry outline, then +transform handles/numeric alternatives and exclusive gesture capture on this +same selection owner. Runtime/archive propagation is separately admitted after +its versioned intent and qualified backend recording contract are integrated. + +## Evidence + +Inspect complete helper paths, list-only enumerate, then one fully qualified +--exact UI test per command. Tests may use literal force documents, mesh summaries, +local Context/input/shapes and freeze/validation only. No imports, simulation, +Worker, history capture, filesystem fixture/cleanup, model or external process. +Cover add/select/remove, canonical IDs and exhaustion, property units/validation, +context/collapse retention and launch-refusal policy. Focused locked compile, +strict Clippy/fmt and independent source review precede commit/publication. +Native validator later checks actual widths/scales/keyboard forms and exports +quality85 JPEGs of exact updated source; no native result is inferred here. diff --git a/docs/plans/ui-refinement/reports/scene-authoring-plan-review.md b/docs/plans/ui-refinement/reports/scene-authoring-plan-review.md new file mode 100644 index 00000000..f0484c75 --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-authoring-plan-review.md @@ -0,0 +1,99 @@ +# Independent canonical scene-authoring admission review + +Reviewed 2026-10-20 by `ui_plan_review` in `/workspace/Rheon-ui-scene`, branch +`implementation/ui-scene-integration-20261020`, HEAD +`7318023f04946171d7909119300af76e455c2926` plus the preserved UI and canonical +backend overlays. Subject: `scene-authoring-admission.md`, initial SHA256 +`7618709846988cd208e5fd3497b6efbc75b5ebca33fb8afaf1abb70a7a07fac3`, the +canonical plan/integration contract, and directly affected App/collision/domain +source. The actual `force_field.rs` read here hashes to +`adecbc5f92b89179a2e38885be2acbcddd42df40061f00ffa5d758224ed045a5`. + +Only this review report was authored. No tests, solver/model workloads, GUI, +filesystem fixtures, compilation, commits or publication were performed by the +reviewer. The backend's newer reported remote tip changes evidence/docs only; +this review relies on the transferred canonical source and its reviewed receipt, +not on a new runtime claim for that tip. + +## Disposition + +The authoring-only slice is proportionate and independently implementable: +one canonical ForceScene, one shared checked model/force ID allocation owner, +one context-scoped selection, a right scene list/properties panel, and Add +Directional/Radial/Vortex. No viewport, solver, recording or run-set change is +needed. Existing snapshots remain authoritative and the new authored scene +cannot launch until runtime/custody integration is independently admitted. + +The lead accepted the concrete transaction/scope conditions below before scene +production edits. No unresolved source-independent design blocker remains once +the canonical plan selects this exact next slice and records the preceding T1 +focused-gate disposition. That is a lifecycle/scope update, not another optional +user approval or a reason to broaden design investigation. + +## Conditions and agreed dispositions + +| ID | Concern | Reviewed disposition | +| --- | --- | --- | +| SA1 | Collision import currently commits `collision.mesh` before `show` returns its accepted-change flag. Exhausted shared IDs must not leave replacement geometry unidentifiable or clear history on a failed scene admission. | Lead will retain prior accepted Arc/status before painting the existing collision form. On successful replacement, admit a fresh canonical ID before resetting history; if allocation fails, restore accepted geometry, report refusal and preserve prior ID/selection/history. Removal needs no new ID. Initial CLI import must also perform transactional ID admission. No collision evaluator/importer rewrite is needed. | +| SA2 | The empty authored list is not proof that no physical forces exist: gravity and the existing configured-session-only force form remain separate legacy sources in this slice. | Label the collection `Authored forces`, keep a visible gravity-additive note, and avoid total/no-force physics claims. Legacy retained force metadata stays unavailable; do not reconstruct new force objects from gravity or the current configured draft. | +| SA3 | Canonical plan still selected T1 at initial review, whereas this report supplies a narrower next authoring write set than the former T2/T3 table. | Before editing scene production, record T1's actual focused checks/disposition and set exactly one canonical next slice with the new `scene_ui.rs`/main/test boundaries. Reports remain non-authorizing detail/evidence, not competing plan authority. | + +The transaction described in SA1 preserves old accepted geometry/identity and +history on refusal, while leaving the user's new import settings as draft input +for correction. Test its pure admission/rollback helper with in-memory summaries +or literal geometry if needed; do not exercise filesystem imports in this UI pass. + +## Canonical source and ownership review + +- `ForceScene::freeze` checks version, at most 256 objects, nonzero and force-list + unique IDs, then validates all objects including disabled ones and deep-copies + the document. The UI must additionally enforce its combined model/force + namespace with checked monotonic allocation and explicit exhaustion refusal. +- Names are bounded in UTF-8 bytes. Canonical units are acceleration or force + density; translation/support use metres and activation uses seconds. Quaternion + validation rejects malformed values; the UI must not silently normalize or + impose unrelated physical limits. Use the shared domain directly, not another + evaluator, force schema or backend-specific approximation. +- Zero-strength additions are safe explicit authoring defaults. Invalid draft + values remain visible and cannot launch; a disabled object still validates. + Support, local vector/orientation, radial/vortex sign and time semantics need + concise named help. Numeric field editing supplies the initial authoring path; + spatial picking, outlines and handles are explicitly subsequent slices. +- App's collision controls own mutable import placement plus accepted mesh; + Request retains the immutable run mesh. `SceneUi` may retain IDs/selection and + receive read-only accepted summaries, but must not clone a second canonical + model-property store. Context changes clear selection and stop hidden playback + while preserving history/index. Draft geometry must not overlay retained fluid. +- New authoring must be disabled in retained/active contexts. Retained scene + properties come from the actual Request, with old force provenance explicitly + unavailable. Current draft forces must never appear as recorded run facts. +- Every nonempty authored draft, including disabled/zero-strength objects, must + refuse both preview and configured new launches before path/output/Worker + creation. Preserve legacy frozen reruns as their own immutable intent; they do + not consume the current authoring draft. Keep the blocking reason visible when + the scene panel is collapsed, rather than only inside object properties. +- At narrow widths, compose one setup/scene drawer and call the existing + App-owned run/status/Cancel projection once. Do not create a second action or + worker owner. Panel collapse retains selection and draft values; native focus, + clipping and scaling evidence remains required. + +## Proportionate evidence boundary + +Inspect each complete helper path, enumerate list-only, then execute one fully +qualified `--exact` UI-only test per command. Literal force documents, model +summaries, canonical freeze/validation and egui input/paint are sufficient for +add/select/remove, property editing, ID exhaustion/rollback, context and collapse. +Do not call field evaluation, simulations, Worker, App::update, history capture, +file import, archive publication, external processes or cleanup from these tests. + +Test launch refusal through the pure policy/actual inert control projection and +review the early guards in real launch methods. Do not make a regression test +that might start a workload if its refusal guard fails. Compile/lint and source +review complement those tests; none proves native rendering or force dispatch. + +The separate backend's core recording/accepted-pressure work and upstream +evidence retain their own source/coverage limits. This admission does not accept +post-custody solver/SQLite integration, configured/provider propagation, saved +force replay, native scene interaction or scientific parity. Distinct reviewed +commits and any later draft publication follow the parent's actual gates; no +publication result is implied by this review. diff --git a/docs/plans/ui-refinement/reports/scene-integration-contract.md b/docs/plans/ui-refinement/reports/scene-integration-contract.md new file mode 100644 index 00000000..969e816e --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-integration-contract.md @@ -0,0 +1,206 @@ +# Scene UI and shared force integration contract + +The user requests compact playback, a collapsible right scene inspector, common +tree/viewport object selection, a thin selected-object outline and manipulators +for models/forces. Desktop UI has one implementation owner here. A separate task +owns shared force physics and solver adapters; parent coordinates its exact handoff. +The parent supplied the exact backend API/semantics from local commit +3e839cbf7c6aca56e8297ef740f7e48543de77b1 (authoring f55cf595, adapters19e0145c, +accepted custody8d2b4f8a), +based on fd7c0f0. Exact published source1d44c8429701f205f678b602040d4f62ea6457b3 is now fetched, +sealed and integrated in isolation; see backend-integration-receipt.md. Runtime +dispatch/desktop frozen intent are still unimplemented. +No UI implementation may invent an alternative domain or claim runtime support. + +## Verified current source and boundaries + +Source baseline:7318023 plus preserved M2a and reviewed UI-5 overlay, precision +tree3e1fd57146e2f1a02abe22a09ac317497f39d738. Main desktop timeline has an explicit +106px minimum and directly owns existing playback fields. Viewport shows one +accepted collision mesh and emitter; collision controls separately retain draft +path/scale/translation and transactional accepted Arc. Recorded particle +selection belongs to Inspector. Those facts are not a general scene graph. + +Current library BodyForce is a stationary MAC-box vector: acceleration m/s² or +force density N/m³, optional world half-open box, sampled on component face +lattices with constant carrier density. Particle configuration has gravity; +that does not establish equivalent general force support. UI must not invent +particle N/m³ conversions, point-force semantics, support sampling or per-backend +approximations from these separate APIs. + +ConfiguredControls also already retains a configured-session-only vector/units/ +box force draft and publishes its existing ForceSpec. T3 must serially migrate +that form/bridge to the shared canonical authoring owner, preserving saved intent +and archive interpretation. Do not add another disconnected copy of those fields +or represent their current configured-only support as particle parity. + +| Owner | Exclusive responsibilities/files | Integration boundary | +| --- | --- | --- | +| Desktop UI task | src/bin/rheon-desktop.rs scene composition, Request/freeze/launch and run_material dispatch; src/bin/desktop/carrier_execution.rs dispatch; new scene/property/manipulator modules, viewport.rs and timeline presentation | Allocate shared IDs, project canonical scenes, freeze once before worker/output creation. Parent confirmed ownership; runtime regions are reserved, not edited in T1. | +| Shared force task | Cargo.toml, src/lib.rs, src/force_field.rs, src/forces.rs, src/simulation.rs, src/particle_fluid.rs, force contract tests/docs; proposed src/material_session.rs and src/recording.rs / src/recording/{carrier,material,provider}.rs custody | Canonical domain/adapters and success-qualified accepted pressure/control custody. Coordinate each newly needed file before edits; no desktop UI ownership. | +| Desktop UI task: confirmed versioned custody | src/bin/desktop/run_sets.rs, run_sets/configured.rs, run_sets/source.rs, run_sets/configured/archive.rs; main Request/history bridge | Parent confirms UI ownership of desktop frozen intent/load/rerun. Backend owns core recording only. Re-admit exact source regions after isolated integration; no such edits in T1. | +| Inspector validation owner | Separately validated Inspector projection/archive work | Recorded probe identity stays separate; revalidate exact source before overlapping integration. T1 edits none of it. | + +UI stages are serial; re-admit shared main/viewport/scene regions before each +stage. No speculative merge/rebase/publication. The exact force source handoff is received; source verification and +per-slice admission govern subsequent editing. T1 is independent and can proceed. + +## Supplied canonical force domain and runtime gate + +Canonical types are ForceScene(version=1, objects), SceneObjectId(u64), ForceObject, +ForceTransform, ForceUnits, ForceKind, ForceSupport, ForceTime and ForceSnapshot. +UI allocates nonzero IDs unique across models and forces. Rename/reorder/save/load +retain IDs; duplication gets a fresh ID. Particle IDs are a separate namespace. +Backend freeze checks force-list uniqueness; it does not provide UI allocation. +Before admitting a loaded scene, validate IDs across its model and force objects +against the whole retained draft. Reject collisions atomically with a visible +reason; never silently remap loaded IDs. The allocator uses checked u64 increment, +starts above the greatest admitted ID, never issues zero and rejects exhaustion +instead of wrapping/reusing an identity. Replacement/import/duplication allocation +must pass this same whole-scene check. Save/load preserves exact admitted IDs. +At most256 forces and256 UTF-8 bytes per name; disabled objects also validate. + +Translation/support dimensions are metres. Rotation is local-to-world quaternion +[x,y,z,w], squared norm within1e-12 of one; malformed values are rejected, never +silently normalized. No scale, parenting, animated transform/strength or mesh +attachment in v1. Local q=Rᵀ(p-c); vector rotates back by R. Directional uses its +local vector; radial is signed constant magnitude (positive repels, centre zero); +vortex follows right-hand local +Y (+X points toward -Z, axis zero). No inverse +square approximation. Support Global, Sphere (outer surface excluded), or Box +(local half-extents, -h<=q>, produced from the canonical ForceScene before worker +or output creation and reused for the entire run. History/rerun/provenance retain +that exact scene under an agreed versioned specification. Empty scenes continue +the existing legacy solver calls. Nonempty authored scenes explicitly refuse +launch until the selected workflow has snapshot dispatch AND qualified recording +custody; no silently ignored forces and no direct solver bypass of session owners. + +Initial d7bffdd5 metadata disclosed a missing MAC force-pressure stamp, so no UI +wiring was attempted. Parent now reports8d2b4f8a/3e839cbf supplies separate +accepted_force_interval_pressure(), accepted_force_controls(), force-aware run +metadata/publication wrappers and binding validation. Exact source has now been fetched +and sealed in isolation; review the receipt before using these APIs. Do not attach a misleading +legacy stamp or bypass a session/provider owner. Historical glyphs use recorded +scenes, never current drafts; preserve old hashes and explicit absence metadata. + +Reported host sequence: freeze one snapshot; build RecordingRun then call +with_force_fields before recorder creation; use carrier/material publication +wrapper for constructor; step through authored-force adapter; publish through +existing qualified recording owners using the expected snapshot. Wrapper and +writer/reader checks must reject missing/mismatched scenes. Metadata v1 is bounded +to262144 bytes and has a digest-bound configuration suffix and named u8 field; +verify exact source/schema instead of duplicating its codec in desktop. + +The parent reports five exact bounded adapter tests passed once from the sealed +PRE-CUSTODY binary0042e68e..., not the latest source. Seven pure in-memory codec +tests and static checks cover the new custody source. No post-custody solver, +SQLite or desktop integration execution; do not apply the older solver results +to the custody changes. Session/configured/provider/transport/free-surface force +propagation remains unsupported and must refuse rather than silently ignore forces. + +Parent has confirmed desktop runtime and versioned frozen-intent/load/rerun file +ownership above. Source transfer/verification is complete. Precise +per-slice admission and runtime integration remain dependencies; no such runtime changes occur in T1. +Pure scene authoring can now consume the transferred canonical ForceScene; +unsupported run dispatch remains visibly refused until its selected owner path is integrated and qualified. + +## Scene selection and properties contract + +App owns one typed object key plus explicit Draft/Frozen-run context. Current +supported objects can use distinct Domain/Emitter/accepted-model keys; future +forces and the model use the canonical SceneObjectId allocated by the UI. There is currently one collision model, not +an invented multi-model collection. Tree, viewport and properties emit selection +or edit intent for that same key; neither stores a second canonical property set. +Missing/deleted objects clear selection. Panel collapse retains it. + +The singular accepted-model slot needs an explicit import/replacement generation; +neither a row index, path string nor Arc pointer is stable authoring identity. +Reimport/replacement clears a selection for the old generation instead of silently +retargeting edits. The UI allocates the canonical domain SceneObjectId across models and forces; +no competing ID type or row-index surrogate. A model replacement gets a new ID. T2 must settle and test these identities before changing scene files. + +During work, project actual frozen Request geometry/forces and disable edits; +retained next-run draft values cannot masquerade as active objects. Recorded +cell/particle probes retain Inspector's separate identity and do not select an +authoring force/model. New model/force authoring is not archive field editing. + +This rule also covers paused/completed/cancelled retained runs: current viewport +code keeps reading running_request after worker completion. Worker presence is +not the scene-context discriminator. Use explicit Draft versus Retained-run +context tied to that immutable request/revision. Switching to Draft shows draft +geometry without retained fluid/run geometry, preserves saved history/index and +clears cross-context object selection. Switching back restores the retained view; +do not overlay editable next-run transforms on historical fluid. This is a T2 +admission prerequisite, not a claim that the switch is implemented today. + +Properties group Transform, supported object-specific values and named optional +advanced/provenance details. Preserve units and concise visible blocking errors. +Named controls and numeric keyboard alternatives accompany axis handles; no +unexplained icon-only scientific controls. Right panel has a labelled collapse +affordance. At narrow widths switch between setup/scene drawers while keeping +the App-owned launch/status/Cancel strip and a usable viewport visible. + +## Picking, outline and manipulation contract + +Gesture priority: captured manipulator, scene-object pick, recorded probe, camera. +Consume one gesture once. Empty-space selection/clear policy and visibility +through fluid must be explicit and tested. Draw a thin screen-space outline that +follows actual selected geometry/support; retain matching selected name/type in +tree/properties so colour is not the only affordance. + +Preview transforms use world metres and the current scene context; normalize only +for viewport projection. Escape discards the preview, release validates and commits +one draft authoring edit, and validation failure retains prior draft/accepted +values with a reason. Model handle release commits draft placement only; accepted +collision geometry still changes through explicit transactional Apply. +Existing model import only supports scale/translation: do not expose working +rotation handles without implementing/reviewing that geometry capability. +Draft model placement remains separate from accepted collision geometry. Keep +transactional Apply/rejection/history consequences; do not reread arbitrary source +files on every drag or silently replace accepted physics with a render preview. + +## First independent slice and evidence + +T1 extracts only existing timeline painting/input into App::timeline_ui with +explicit selected frame/count arguments. It must not call selected_frame, +Inspector.ensure_timestep, History capture/view, Worker, viewport show or numerical +lab initialization. Remove the106px floor; preserve all playback/track/step/time/ +calculation labels, ranges and transitions. Pinned egui bottom panels reserve height +before painting; floor removal alone demonstrably clips cold frames. Admit a local +invisible/disabled sizing Ui with disjoint IDs and a disposable six-scalar playback +projection. Measure the same frozen display context and Frame, then reserve exact +measured height and commit interactive controls once. No App.update replay/global +discard, history reads or persistent parallel playback state. Natural wrapping may +grow when content needs space; idle must not reserve empty height. + +Reserve the actual preceding280px sidebar using an inert SidePanel in layout +tests (900/1250 window widths leave about620/970px for the strip). The <=70px +criterion applies to empty idle state; selected captions can naturally wrap/grow. +Use only inspected, list-enumerated, individually named --exact UI tests. Construct +an inert PreviewFrame directly when needed; never capture it from a simulation. +App default is inspected and does not initialize a numerical lab; never call +App.update in these tests. Test actual controls, narrow/wide panel geometry and +visible units/context. No filesystem fixtures/cleanup, model/numerical workloads +or genuine cancellation. Independent review precedes further UI integration. + +Native validation remains separate: actual updated idle screens at1250×820 and +900×650,100%/150%, with retained quality85 JPEGs labelled by exact source/tree and +state. No historical screenshot or headless layout is native proof; image delivery +must have confirmed attachment identities. This idle procedure requires no solver +run/cancel workload. Full new scene/manipulator acceptance waits for T2–T4 and the +shared domain handoff; T1 supplies neither force parity nor scientific acceptance. diff --git a/docs/plans/ui-refinement/reports/scene-plan-review.md b/docs/plans/ui-refinement/reports/scene-plan-review.md new file mode 100644 index 00000000..72db23ad --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-plan-review.md @@ -0,0 +1,169 @@ +# Independent scene and timeline plan review + +Reviewed 2026-10-20 by `ui_plan_review`. Source: local HEAD +`7318023f04946171d7909119300af76e455c2926` plus preserved M2a and the reviewed +UI-5 overlay, whose frozen production checkpoint is reported as tree +`3e1fd57146e2f1a02abe22a09ac317497f39d738`. Reviewed the revised canonical +plan, scene integration contract and actual timeline, viewport, collision, +force, camera, history and recorded-probe ownership. Only this report was +authored. No runtime test, GUI, solver, model or filesystem fixture was run. + +## Disposition + +T1 is a suitable independent reversible slice: extract the existing timeline +painting/input boundary and remove its 106-pixel height floor while preserving +all controls, captions, ranges and transitions. It does not depend on the force +domain or authorize scene/physics changes. The lead incorporated all findings +below before production edits; no T1 plan/design blocker remains. No further +optional user approval or broad design investigation is needed for T1. + +The scene/force direction is appropriately separated into later admissions. +The shared force API is explicitly pending, not agreed or inferred from existing +MAC forces/gravity. T2–T4 must resolve their context/identity/edit boundaries +before overlapping production edits. M1/native/global accessibility and the +previous delivery limitations remain unsatisfied. + +## Findings + +| ID | Severity / scope | Finding and required disposition | +| --- | --- | --- | +| SP1 | Medium / plan conformance | The revised simplicity review shortened the eight canonical probe labels and the T1 milestone uses `Admitted`, outside Planning's declared lifecycle. Restore the pinned source validator's exact probe labels and use `Planned` or `Active` as appropriate; run the already established structural check as supporting evidence. A structural pass is not design/engine certification. | +| SP2 | Low / T1 evidence | S1's height criterion needs an exact state and width boundary. Define the <=70 logical-pixel target for the empty idle strip, not every paused/selected state with all captions. Reserve the actual preceding 280-pixel setup sidebar when testing 900/1250 window widths, rather than giving the timeline the entire window. A simple inert SidePanel reservation supplies that layout without calling the real sidebar. Selected-frame/long-track captions may naturally grow; test no clipping or lost units without a new fixed cap. | +| SP3 | Medium / before T2 | `worker.is_some()` cannot define scene context. The current viewport continues using `running_request` geometry/domain/emitter after completion or cancellation while historical frames remain, but next-run setup is editable. Specify Draft versus frozen/retained-run viewing through paused/completed/cancelled history and explicit scene switches, plus selection disposition. Otherwise editable draft properties can represent different geometry from the highlighted historical object. This does not block T1. | +| SP4 | Medium / before T2 | The accepted-model key needs an import/replacement generation. Current collision state is one replaceable mesh slot; path, row index, Arc pointer or a reused singleton `Model` key does not establish stable identity. Reimport/replacement must invalidate or explicitly transition selection rather than silently retarget an edit. App can observe the existing accepted-change result without giving viewport/properties a second geometry owner. | +| SP5 | Medium / before T4 | Clarify release/Apply semantics. Handle release commits a validated draft placement or authored-force edit; it must not implicitly replace accepted collision geometry or mutate active physics. Accepted model geometry still changes through explicit transactional Apply, existing rejection/retention and history effects. A drag does not reread/import a file per frame. | + +Resolution check: all five findings are incorporated in the revised plan/contract. +The exact eight probe labels and T1 `Active` lifecycle are restored. S1 now names +empty idle state and actual sidebar reservation. Draft versus Retained-run is +explicit independently of Worker presence; switching clears context-dependent +selection. Accepted-model generation and draft-only handle-release/explicit Apply +semantics are stated. The contract also identifies the existing configured-only +force form and assigns its future T3 migration rather than allowing a second +canonical force form or falsely claiming particle support. + +Final reviewed plan SHA256: +`b79c5eb98eabebd5de8372cc404a2f328195fe4375280889677ed33631618fc1`. +Final reviewed integration contract SHA256: +`7016698534bb32983cc80bf22a663e0c4bcba233860acc339ca022b34620ba82`. +The timeline production source remained unchanged at this resolution check. +Structural-validator execution remains the lead's supporting gate; this reviewer +ran no tests and does not claim engine certification. + +## T1 implementation and evidence boundary + +The current block at `src/bin/rheon-desktop.rs:2144` owns Play/Pause, stepping, +Live, playback speed, track selection, scrubber and selected-frame captions. +Keep those transitions together in `App::timeline_ui` with explicit selected +frame/count arguments. The method must not query a new frame, history, Worker, +Inspector, numerical lab, filesystem or model. Remove only the reserved height; +moving scientific captions to details is outside this first slice. + +Use directly constructed inert `PreviewFrame`/Request values and local egui +Context/input for the exact new tests. `App::default` does not initialize the +numerical lab, but `App::update` is outside this test boundary: +`selected_frame` can invoke `Inspector::ensure_timestep`, and viewport mode +initialization can compute independent numerical state. No captured simulation +frame, LiveHistory, archive, subprocess or disposable output is needed. + +Inspect each complete test/helper path, enumerate list-only, then run one fully +qualified `--exact` allowlisted UI name per invocation. Preserve the previous +incident and failure logs; do not repeat a prefix suite or cleanup. Test actual +transport/Live/scrub input, initial/end stepping and track reset behavior using +inert state. Native geometry/focus evidence remains separate from headless +input/paint results. + +## Ownership conclusions + +- App owns one ephemeral typed selection within an explicit scene context. + Tree, viewport and properties use that key and emit intent; they do not keep + competing canonical object properties. Panel collapse retains selection. +- Recorded particle/cell IDs remain Inspector/probe state, distinct from scene + authoring IDs. The right authoring inspector does not replace numerical + archive inspection or create missing archived object provenance. +- The force owner supplies canonical identity, units, support/boundary convention, + activation/time, transforms, validation, freeze and both-backend semantics. + Existing `BodyForce` is MAC-specific; UI cannot infer particle N/m³ conversion, + point-force behavior or support sampling. A whole-box uniform-force visual + anchor must remain distinct from physical localized support. +- The UI task exclusively owns desktop composition/input. The force task must + supply a pinned reviewed domain handoff without concurrently editing desktop + files. Serial shared-region re-admission is appropriate; no speculative merge + or duplicated force adapter is justified. +- Existing collision draft path/scale/translation and accepted immutable mesh + are distinct truths. The current one-model capability is stated honestly; + no placeholder multi-model collection or unsupported rotation is advertised. +- Captured handle gestures precede object picking, recorded probes and camera + input. A future outline follows actual selected geometry/support and has a + matching named tree/property selection; colour alone is insufficient. +- The narrow-screen single-drawer/global-run-strip proposal belongs to T2 and + needs actual native space/focus evidence. It must not expand T1 or weaken + existing status/Cancel visibility. + +The replacement design has the required proportionate direction and concrete +ownership. Source-only standards review and an honest missing domain/display +contract support the independent T1 work; they do not accept the larger scene +authoring feature, force parity, scientific behavior, publication or cleanup. + +## Follow-up: supplied force metadata and bounded T1 sizing + +The updated plan/contract now attributes canonical force metadata to parent +handoff `d7bffdd5d6e80e022f15c59d3574739e3b39b352`, including its authoring/adapter +lineage. Exact source transfer remains pending. This review cannot verify those +new types or adapter semantics from the current main-derived checkout and does +not promote the metadata or unexecuted integration cases to runtime parity. + +No source-independent T1 admission blocker remains. The observed floor-only +cold-frame clipping supports a narrowly scoped local sizing pass: one disposable +copy of the six existing playback scalars, a disjoint-ID invisible/disabled Ui, +the same frozen display context/Frame, then one real interactive paint and one +commit back to App. Pinned egui 0.33.3 `UiBuilder::invisible()` explicitly disables +the Ui. This does not justify an App replay/discard loop, persistent duplicate +playback state, numerical history reads or Worker access. Exact cold, changing- +caption and actual-input regressions remain required; this source/admission +review ran none and does not claim their results or native appearance. + +The minimal proposed `Option>` retains a single immutable +snapshot rather than copying mutable authoring state per step. Validation/freeze +must precede Worker/output creation. Keeping empty scenes on the old solver path +is important because the reported new MAC path lacks the pressure stamp consumed +by current `carrier_publication`; current source explicitly rejects a non- +constructor state without success-qualified interval pressure. Do not route even +an empty scene through the unqualified new step method. Nonempty authored scenes +must visibly refuse a selected workflow until both dispatch and accepted +pressure/control/recording custody are qualified. Preserve configured-session, +particle/session and transport owners rather than bypassing them to make a new +step callable. + +Runtime/custody ownership is correctly marked proposed/reserved, not permission +to edit. Parent confirmation of exact file ownership and a reviewed source +handoff remain required before runtime or persisted-spec/digest changes. In +particular, configured execution currently passes through `ConfiguredTrack` and +its accepted-session/provider owner; it must remain unsupported for new authored +forces until that path has an explicit agreed disposition. The current T1 +production changes do not edit any of these boundaries. + +The additional scene-stage ID requirement is incorporated in the latest contract: +import checks cover model and force IDs against the whole retained draft, reject +collisions atomically without remapping, and use checked u64 allocation above +the admitted maximum with no zero/wrap/reuse and explicit exhaustion rejection. +Save/load preserves exact IDs. The reported backend freeze checks uniqueness +inside the force list only; the UI retains the combined-namespace obligation. +This supplies a coherent admission contract, not proof of its future implementation. + +Updated admission reviewed at plan SHA256 +`a28d43ea72b6ddb3f5735f3f7062ae6d34301c729e7a82a80c39e37f78fe18da` +and integration-contract SHA256 +`9d14082474e368a8dd71e38615d19f4526c07e37cc41ba472921ee063b9a4bcd`. +Earlier reviewed hashes above retain their original scope. No test, numerical, +model, GUI, filesystem fixture, cleanup or canonical-plan mutation was performed +by this reviewer; only this follow-up report changed. + +Final metadata/sizing admission disposition: no remaining source-independent +blocker to T1. Latest contract SHA256: +`2a51b777e23b104548cbeb4e42eb38ecb7a4c52f4228aa46ff653a4291f0cc35`. +The lead's `/tmp/rheon-ui-scene-plan-structure-3.log` records the pinned structural +check passing; this reviewer inspected that result without executing it. Exact +domain source transfer, parent-agreed custody ownership, workflow-qualified +dispatch/recording and native evidence remain gates on their own later claims. +Nothing in this disposition admits runtime/custody edits or a force workload. diff --git a/docs/plans/ui-refinement/reports/timeline-compact-checkpoint.md b/docs/plans/ui-refinement/reports/timeline-compact-checkpoint.md new file mode 100644 index 00000000..f0d20b10 --- /dev/null +++ b/docs/plans/ui-refinement/reports/timeline-compact-checkpoint.md @@ -0,0 +1,120 @@ +## Completed focused source follow-up + +The parent authorized completing/committing this slice. Final source replaces +playback-speed DragValue with a bounded, labelled TextEdit plus × units, finite +parsing, the same0.1–4 clamp and Up/Down0.1 adjustment. Invalid text retains the +last valid canonical value and visible error. The transient buffer is separate +from the canonical scalar, not a run input. Focus/Tab without edits preserve bits. + +Measurement remains invisible/disabled/disjoint and hidden from AccessKit, but +uses ordinary layout rather than sizing_pass. The report-state exact test exposed +that sizing_pass changes child alignment and Separator geometry; retained failure +/tmp/rheon-ui-timeline-complete-exact-track-1.log. Ordinary layout fixes this without +global application replay. Final idle height is48px, not the historical47px below. + +Four exact inspected UI-only tests pass independently in +/tmp/rheon-ui-timeline-complete-exact-{idle-2,controls-2,speed-2,track-2}.log. +They add actual speed focus/label/Tab/edit/4096-character invalid text, clamps, +ArrowUp and native-method track pointer selection plus literal report caption +stress. Reports combine synthetic presentation values, not numerical evidence. +Strict Clippy passes53.53s. Final check/fmt/diff logs use the matching complete- +prefix. No broad test, solver/model/workload, file fixture/cleanup or rerun occurred. + +The focused speed geometry limit below is resolved in this follow-up. Native +rendering, screen-reader interaction, long configured-member labels, popup keyboard +navigation/window bounds and broad whole-app acceptance remain unverified. This +completes the scoped T1 source increment, not native/full-plan/M1 acceptance. + +--- + +# Compact timeline checkpoint — source and isolated UI evidence + +2026-10-20. Uncommitted continuation of UI-5 precision tree +3e1fd57146e2f1a02abe22a09ac317497f39d738 on local HEAD +7318023f04946171d7909119300af76e455c2926. This is a bounded T1 checkpoint, +not complete timeline/native/scene-force acceptance. + +## Change and result + +The empty timeline previously reserved106 logical pixels. At both900px and1250px +windows with an inert280px setup sidebar, isolated egui tests now measure47px +(620px and970px available strip width). The existing controls, playback range, +step/time/calculation captions and transitions remain. Fonts were not reduced. + +App::timeline_ui extracts painting/input from the existing update site without +calling selected_frame/history/Inspector/Worker/viewport. Bottom-panel geometry +is measured locally using the same Frame/contents and frozen display caption. +A temporary six-scalar copy, disjoint widget IDs and invisible/disabled Ui keep +measurement from committing input. Only the real interactive copy writes back to +App, once. No global discard or App.update replay. Track caption changes project +on the next frame. Measurement's AccessKit subtree is explicitly hidden; tests +check that only one real Play/Pause control remains outside that subtree. + +## Deciding and focused evidence + +Every helper path was inspected before list-only enumeration and each test was +individually selected with its fully qualified name and --exact. No solver, +numerical/model fixture, archive/history capture, subprocess or filesystem cleanup +is reachable from these tests. PreviewFrame is literal inert presentation data. + +- Before: /tmp/rheon-ui-timeline-before.log fails the height target at106px. +- Floor removal alone: /tmp/rheon-ui-timeline-after-1.log fails cold panel bounds; + this failure is retained, not hidden by warming egui caches. +- Final enumeration: /tmp/rheon-ui-timeline-test-list-after-3.log lists170 tests; + enumeration does not execute them. +- timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: + /tmp/rheon-ui-timeline-after-3-exact-1.log,1passed,0.01s; cold/warm geometry, + visible labels and measurement/real accessibility tree at both widths. +- timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: + /tmp/rheon-ui-timeline-after-3-exact-2.log,1passed,0.03s; cold selected and + empty-to-selected caption bounds, real pointer Step/Play/Pause/Follow live, + retained physical-time and step-calculation captions. + +Locked desktop check, strict desktop/test Clippy, formatting and whitespace logs +pass. Logs are /tmp/rheon-ui-timeline-final-{check,clippy-1,fmt,diff-check}.log; +check2.43s and Clippy29.31s, no diagnostics; formatting/whitespace clean. Independent source +review is reports/timeline-source-review.md; plan review is scene-plan-review.md. + +## Explicit acceptance limits + +Pinned egui DragValue paints a button in disabled measurement but a TextEdit at +the real focused ID, using that ID's private edit buffer. Disjoint measurement +IDs cannot observe the live buffer, so identical values/contents do not prove +identical focused-input geometry. The current two probes do not cover playback +speed editing, arbitrarily long/invalid editor text or a carrier track dropdown +near a row boundary. General measured-height safety in these states remains +unaccepted; review/repair with another exact inert probe before full T1 acceptance. +The passing cases have no running_request, particle/pressure report or open popup. +Do not generalize them to all result/error text, keyboard/screen-reader usability, +150% DPI or native rendering. No local DISPLAY/WAYLAND exists. + +## Native handoff, no workload + +Use the exact incremental tree/patch from this checkpoint over its verified UI-5 +prerequisite, retain all earlier evidence and verify hashes before building. +Launch the existing desktop binary without starting any simulation/model. At +1250×820 and900×650,100% and150%, inspect idle timeline bounds/labels, pointer and +keyboard speed focus/edit, sidebar scrolling and close/reopen. Verify focused +long/invalid speed input separately; report it without silently expanding scope. +Carrier track/result/report cases require a separately approved inert fixture or +existing retained source-qualified recording; do not create one by running a solver. +Actual native results remain pending. Export actual updated rendered screens as +JPEG quality85 (adjust only for text readability), preserving revision/tree/state, +window/scale and confirmed attachment identities. No mockup/historical capture +or headless shape output is a substitute. This idle handoff needs no run/Cancel +workload. Genuine cancellation and old M1 gaps stay parked. + +## Preservation and next dependency + +Original M2a/settings and incident log hashes are unchanged. UI-5 precision is +frozen separately and unchanged by this slice. The accidental broad numerical +fixture execution remains an incident; successful-test teardown removed those +disposable outputs before detection, with no measured resources/scientific credit. +No source commits, remote changes, publication, cleanup or new workload occurred. + +Next scene work uses the supplied canonical ForceScene/SceneObjectId contract; +exact backend source transfer is pending locally. UI runtime Request/dispatch and +shared versioned archive custody regions now have parent-confirmed UI ownership. +They remain reserved for isolated source integration and qualified recording, +not silently edited by T1. Parent reports new backend custody APIs, but its five +solver results apply to pre-custody source; no post-custody integration credit. diff --git a/docs/plans/ui-refinement/reports/timeline-source-review.md b/docs/plans/ui-refinement/reports/timeline-source-review.md new file mode 100644 index 00000000..07e28acc --- /dev/null +++ b/docs/plans/ui-refinement/reports/timeline-source-review.md @@ -0,0 +1,265 @@ +# Independent T1 timeline source review + +Reviewed 2026-10-20 against frozen UI-5 tree +`3e1fd57146e2f1a02abe22a09ac317497f39d738` in `/workspace/Rheon-ui-precision`. +Only this report was authored by the reviewer. No tests, numerical/model/native +workloads or filesystem cleanup were executed by the reviewer. + +## Disposition + +The source supports an **uncommitted, partial T1 checkpoint** for the tested +empty/simple-selected states. T1 acceptance remains open. The 106 px floor is +removed, existing playback controls/units are retained, and the reviewed exact +probes establish a 47 logical-pixel empty strip at both tested widths. This is +not a general all-state layout-safety result. Focused playback-speed editing, +track dropdowns, native interaction and screen-reader acceptance remain +unaccepted, for the concrete reason below. + +## Implementation review + +`TimelineControls` is a disposable six-scalar paint projection; App remains the +owner of step, track, Follow live, playing, playback time and speed. The shared +contents function preserves the existing transport, scrub, speed and track +handlers, physical-time/calculation captions, particle mass/temperature and +pressure/divergence details. No history/worker/recording/backend owner is copied +or dispatched. `App::update` calls the extracted helper at the original position, +after sidebar navigation and before viewport composition. + +The initial floor-only change produced a real cold-frame regression: egui's +bottom panel reserved cached/default height before drawing larger content. +The repair locally measures the same frame/contents with +`UiBuilder::sizing_pass().invisible()` and a separate scalar copy, then reserves +the measured height for the real panel. Invisible also disables input in pinned +egui 0.33.3. Measurement uses a disjoint `timeline-sizing` ID; the real panel +retains `timeline` and `timeline-track`. There is no global discard/replay of +`App::update`, clock advancement, poll, sidebar, history or viewport work. +Only the real control projection is committed once after interactive painting. + +The track caption is frozen across measurement and paint; a newly selected track +is projected on the next frame. This prevents measuring one caption and painting +another after input. Default frame/style and available width are shared, and +height is rounded upward rather than imposing a new permanent minimum. + +Invisible disabled widgets still register AccessKit nodes. The measurement root +is explicitly marked hidden through the public local node API, whose pinned +contract excludes the root and descendants from assistive-technology presentation +and hit testing. No global AccessKit toggle is introduced in production. + +## Complete inert-path inspection + +Both fully qualified tests call only local Context/input/paint helpers and +`App::timeline_ui`, with a preceding empty 280 px SidePanel. The selected-frame +fixture directly constructs presentation data with empty density/no reports; +it does not capture or generate numerical evidence. + +The full App default chain was inspected, including configured/saved comparison, +archive Inspector state, ExportFile string constructors, collision/material, +emitter/turbulence, camera/viewport and empty particle defaults. These create +values/empty owners. `available_threads` queries OS parallelism without starting +threads. Worker/history/request are None and asserted after every test frame; +App Drop therefore has no worker to cancel/join, nor a fixture cleanup path. +Tests never call update, selected_frame, history/view/capture, viewport rendering, +simulation/model/Worker startup, archive open/export, process execution or file +output. AccessKit is enabled only on the local headless Context, and its new +helper traverses finite output-tree nodes with a visited set. + +## Reviewed evidence + +The owner enumerated 170 tests list-only and selected only these individual +`--exact` names: + +- `timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar` +- `timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time` + +The retained before log records 106 px idle height. The floor-only after log +records the first-paint panel-bounds failure; neither is relabeled as success. +Reviewed final `/tmp/rheon-ui-timeline-after-3-exact-{1,2}.log`: one test passes +per invocation. Windows are 900/1250 logical pixels wide, with 280 reserved for +the sidebar, leaving 620/970 for the strip; the headless height is 650. Empty +idle is 47 px tall at both widths. Cold/warm empty, cold simple-selected and +empty-to-selected text/panel/clip bounds are checked. Actual pointer input checks +Step, Play/Pause and Follow live state transitions, with synthetic physical-time +captions of 40 ms simulated and 2 ms calculation time. This does not exercise +continuous playback/history integration. + +AccessKit output checks the hidden sizing subtree and exactly one unhidden real +Play/Pause control. This is tree-structure evidence, not a screen-reader session. +Final strict Clippy and affected check logs pass; the owner reports formatting +clean, and reviewer `git diff --check` passes. No additional tests were run by +the reviewer. + +## Open source/evidence limits + +Disabled sizing renders playback speed as a compact DragValue button. The real +focused control instead renders TextEdit from the real ID's cached edit buffer; +disjoint measurement IDs cannot inspect that buffer. Consequently identical +scalar values/shared contents do **not** prove identical focused-editor geometry. +Longer edit text or the carrier track selector near a wrapping boundary may need +more room than this disabled projection measured. The present exact tests do not +exercise speed focus/edit, long track captions, popup interaction or their +combined geometry. They cannot close that source assumption or establish +all-state measured-height correctness. Record it in the plan/handoff and validate +or repair it before claiming full T1 layout safety. + +Native sizes/scales, full application focus order, assistive technology, +representative usability and matched-state word reduction remain unverified. +Force physics/scene selection/property integration and parked live cancellation +are outside this checkpoint. The earlier broad-filter incident retains its +original scope and supplies no evidence here. + +## Reviewed identity + +| File | SHA256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `c0f7b12a2b75b95bb4c70da920b343da36658fc6c766b4f156bf51af88e57695` | +| `src/bin/desktop/timeline_ui_tests.rs` | `12bee2a70ab3b6cb40a73e183e0786d220ed516761c416bafa7ca097c64352fa` | +| Exact allowlist | `9db83063d55f5a5b8db77b30ce2b49f31d0d8235f84b9b231a3e2bc965096fd2` | + +The earlier precision source remains unchanged at SHA256 +`099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121`. + +## Follow-up: bounded speed editor and four-test source admission + +2026-10-20, `/workspace/Rheon-ui-scene`. This follow-up supersedes the focused +DragValue source limitation above for the implementation identified below; +the earlier checkpoint/failure receipts remain historical evidence. Reviewer +performed source inspection only, including the complete four test/helper paths, +and did not execute tests, numerical/model workloads or filesystem cleanup. + +The speed control now uses a clipped single-line TextEdit with a 44px desired +inner width, named `Speed` and associated through `Response::labelled_by` with +its visible label; the multiplier unit remains visible. It retains one canonical +App-owned f64 plus transient edit text/invalid state. The local measurement +projection clones those values for its disposable disabled pass; only the real +pass commits App input. Unlike the removed DragValue branch, pinned egui's +TextEdit builder uses the same bounded width with `clip_text(true)` while +focused or disabled, independently of the full edit galley's width. Scrolling +the actual text within that field is deliberate. This is a visual width bound, +not a hard memory limit on the input string. + +Only a changed buffer parses into the canonical speed. Finite values retain +the 0.1–4 range through clamping; invalid/nonfinite input retains the previous +canonical value with concise feedback. Up/Down changes the canonical value by +0.1 within the same range. Valid nonfocused text is refreshed from that value; +mere focus traversal does not parse/round the f64. The invalid-feedback visibility +is read before input changes, so both passes paint the same prior-state rows; +new feedback appears on the next paint. The playback speed no longer supports +pointer dragging; editing and the documented keyboard increment are the intended +replacement affordances and need native usability validation. + +Two review findings were corrected before the admitted probe run: the initially +unnamed editor received the explicit visible-label association, and the test's +pointer target now intersects the long galley with its clip rectangle. The speed +test verifies the actual focused AccessKit node is TextInput with a label +association before exercising the input. AccessKit tree inspection supports +source/input checks; it does not establish screen-reader acceptance. + +All four full test/helper paths are inert: + +- `timeline_empty_idle_uses_content_height_after_sidebar` +- `timeline_controls_retain_transitions_and_physical_time` +- `timeline_speed_edit_focus_bounds_validation_and_keyboard` +- `timeline_track_selection_and_report_rows_fit_cold_and_changed_states` + +Each is in `timeline_ui_tests`. Context/run helpers reserve only an empty 280px +sidebar and call `App::timeline_ui` directly. App::default's complete owner chain +remains scalar/string/empty-collection construction and absent runtime objects; +its sole OS query is available thread count. Viewport defaults keep numerical +views and the optional scene absent. App Drop joins only `worker: Some`, while +each frame asserts worker/history absent. Nested defaults do not open paths or +perform output cleanup. `RecordingLimits::default` consists of three scalar +limits and allocates no database or recording buffers. + +The new Request is a literal native-method vector, scalars and empty strings; +collision mesh, emitter, material simulation configuration, saved/configured +revisions, receipts and synchronization gates are absent. `track_label` reads +only its native method IDs. Literal StepReport/ParticleStepReport/PreviewFrame +values do not construct a solver, capture a numerical frame, initialize history +or render a viewport. Their deliberately combined carrier/particle captions are +synthetic presentation stress, not proof of a reachable physical run state. +Text helpers, shape/AccessKit traversal, keyboard/pointer events and Ctrl+A use +only the local egui context and retained form/presentation values. They never +call App::update, selected_frame, Worker, recording/archive APIs, a model or a +numerical fixture. No uninspected indirect workload or cleanup path was found. + +The admitted additions check exact speed bits on focus/Tab, finite edits, +nonfinite/4096-character invalid text, recovery, clamping and ArrowUp; they also +open/select the native SGS track and exercise literal report captions in cold +and changed states at 900/1250px window widths. Separate popup layers are +intentionally outside timeline-panel containment assertions. Configured long +member/provider captions, popup window bounds/keyboard navigation, arbitrary +format/report combinations and continuous playback/history remain unaccepted. +Four exact probes cannot establish general all-state measured-height correctness. +Native focus, actual screen readers and both scales remain separate requirements. + +At this source-admission checkpoint, list-only output reports 172 tests; no +runtime result for the new source is claimed here. The implementation owner +will run only each inspected fully qualified name individually with `--exact` +and retain its logs. This review does not authorize a broad filter or additional +numerical/model execution. Earlier exact results apply only to the earlier +checkpoint until current evidence is recorded. + +| Follow-up reviewed file | SHA256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `89cb033b05499a4504abfb6e6f797ffd8d62fb32f945de43b574545ecab2f928` | +| `src/bin/desktop/timeline_ui_tests.rs` | `c2b2a2f7b5be44963d1495914007b16cdae9df961b17a135c793e63ff1aae824` | +| Preserved UI5 `settings.rs` | `099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121` | + +No remaining blocking source finding for these four bounded probes. This is +source admission with explicit evidence gaps, not full T1/native acceptance. + +## Final follow-up: ordinary invisible measurement and exact evidence + +The final reviewed projection removes `UiBuilder::sizing_pass()` and retains +`invisible()`. This is a justified correction to the earlier measurement recipe, +not removal of the disabled interaction guard. Pinned egui 0.33.3 makes an +invisible root disabled (`disabled || invisible`) and propagates that restriction +to children. The measurement still uses a disjoint root ID, explicit hidden +AccessKit root, disposable cloned form values, matching frame/width and one real +App commit. It does not replay App::update or introduce solver/history reads. + +Inspection of pinned `Ui::new_child` confirms that sizing-pass mode changes +cross justification/alignment. `Separator::ui` separately substitutes zero +available space while sizing. Those differences affect the actual wrapped +timeline's row geometry, so the earlier sizing-pass measurement was not fully +equivalent to ordinary painting. The newly added cold synthetic-report probe +exposed a panel-out-of-window failure with that mode enabled. Ordinary invisible +layout preserves the normal layout rules for these contents and removes that +specific mismatch. It remains an offscreen projection, not a general guarantee +that all disabled and active widget states have identical geometry. + +Read retained `/tmp/rheon-ui-timeline-complete-exact-track-1.log`: one selected +test failed its unchanged `panel.bottom() <= 650.5` boundary assertion before +this correction. Read each final exact-result log below: each selects exactly +one named test, reports one pass and 171 filtered out. No test was run by the +reviewer. + +| Final log under `/tmp/` | Checked result | +| --- | --- | +| `rheon-ui-timeline-complete-exact-idle-2.log` | Empty cold/warm geometry and hidden measurement tree pass; idle height **48 logical px** at 620/970px available strip widths, in 900/1250px windows with 280px sidebar reserved | +| `rheon-ui-timeline-complete-exact-controls-2.log` | Cold/changed simple selection, physical-time captions, pointer Step/Play/Pause/Follow-live transitions pass | +| `rheon-ui-timeline-complete-exact-speed-2.log` | Focused labelled editor, unchanged speed bits on focus/Tab, edits/ArrowUp, invalid text including 4096 characters, clamping/recovery and checked geometry pass | +| `rheon-ui-timeline-complete-exact-track-2.log` | Native track selection/reset plus cold/changed literal carrier/particle report caption geometry pass at both widths | + +The tests were re-read in full after the change. No assertion was weakened to +accept the measurement failure: the panel-bound assertion gained diagnostic +text; only the exact editor payload has the intentional clipping exception. +The fixture/helper and App default/Drop boundary remains inert as described +above. The preserved precision source remains unchanged. Reviewer independently +ran only `git diff --check`, which passed; strict Clippy and remaining static +gate outcomes belong to the implementation owner's final receipt. + +| Final reviewed file | SHA256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `86f2d65ed296a5dbdf35887a7f1ae4c03eadfb55c58fc8b77ba3ae9e9ca62a7b` | +| `src/bin/desktop/timeline_ui_tests.rs` | `653848ce12986065b966c6ab2d97b23c2e7da4cdf5ec7f5a6b99c1e5a9ef2c35` | +| Preserved UI5 `settings.rs` | `099fa897a53feee4cb1de483655893f50e6c713c39e66c1a25c8f0549ee2c121` | + +No blocking source finding remains for this bounded timeline increment. The +four current exact probes close the earlier untested focused-speed/native-track +and literal-report cases within their stated conditions. Native screenshots, +OS-level focus, screen-reader behavior, configured long captions, popup bounds/ +keyboard navigation and broader report/state combinations remain unaccepted. +This supports a source-and-focused-test checkpoint; full T1/native acceptance +remains separate. No scene, runtime force, numerical/model or live-cancellation +acceptance is implied. diff --git a/src/bin/desktop/timeline_ui_tests.rs b/src/bin/desktop/timeline_ui_tests.rs new file mode 100644 index 00000000..0a8317c0 --- /dev/null +++ b/src/bin/desktop/timeline_ui_tests.rs @@ -0,0 +1,443 @@ +//! Isolated playback painting/input. Never call App::update, selected_frame, +//! history capture/view, viewport, Worker, simulations or filesystem fixtures. +use super::*; + +fn context() -> egui::Context { + let ctx = egui::Context::default(); + ctx.enable_accesskit(); + ctx.style_mut(|style| { + style.animation_time = 0.0; + style.spacing.item_spacing = egui::vec2(8.0, 7.0); // native app style + }); + ctx +} +fn frame( + ctx: &egui::Context, + app: &mut App, + width: f32, + selected: Option<&PreviewFrame>, + events: Vec, +) -> (egui::FullOutput, egui::Rect) { + let mut rect = egui::Rect::NOTHING; + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, 650.0), + )), + events, + ..Default::default() + }, + |ctx| { + // Reserve the actual setup sidebar width, without executing it. + egui::SidePanel::left("timeline-test-sidebar") + .exact_width(280.0) + .resizable(false) + .show(ctx, |_| {}); + rect = app.timeline_ui(ctx, if selected.is_some() { 5 } else { 0 }, selected); + }, + ); + assert!(app.worker.is_none() && app.history.is_none()); + if let Some(request) = &app.running_request { + assert!( + request.collision_mesh.is_none() + && request.material.is_none() + && request.emitter.is_none() + && request.saved_set.is_none() + && request.configured_set.is_none() + && request.manifest_receipt.is_none() + && request.progress_gate.is_none() + && request.failure_gate.is_none() + ); + } + (output, rect) +} +fn text_rect(output: &egui::FullOutput, label: &str) -> egui::Rect { + output + .shapes + .iter() + .find_map(|s| match &s.shape { + egui::Shape::Text(t) if t.galley.text() == label => { + Some(egui::Rect::from_min_size(t.pos, t.galley.size()).intersect(s.clip_rect)) + } + _ => None, + }) + .unwrap_or_else(|| panic!("missing {label}")) +} +fn assert_visible(output: &egui::FullOutput, panel: egui::Rect) { + assert_visible_with_editor(output, panel, None); +} +fn assert_visible_with_editor(output: &egui::FullOutput, panel: egui::Rect, editor: Option<&str>) { + assert!( + panel.top() >= 0.0 && panel.bottom() <= 650.5, + "panel outside bounds: {panel:?}" + ); + let tree = output.platform_output.accesskit_update.as_ref().unwrap(); + let sizing_id = egui::accesskit::NodeId(egui::Id::new("timeline-sizing").value()); + let sizing = &tree + .nodes + .iter() + .find(|(id, _)| *id == sizing_id) + .unwrap() + .1; + assert!( + sizing.is_hidden(), + "sizing controls must not be exposed to AT" + ); + let mut hidden = std::collections::HashSet::new(); + let mut pending = vec![sizing_id]; + while let Some(id) = pending.pop() { + if hidden.insert(id) { + let node = &tree.nodes.iter().find(|(key, _)| *key == id).unwrap().1; + pending.extend_from_slice(node.children()); + } + } + let real_play_controls = tree + .nodes + .iter() + .filter(|(id, node)| { + !hidden.contains(id) + && !node.is_hidden() + && matches!(node.label(), Some("▶ Play" | "❚❚ Pause")) + }) + .count(); + assert_eq!( + real_play_controls, 1, + "one accessible real playback control" + ); + for shape in &output.shapes { + if let egui::Shape::Text(t) = &shape.shape { + let mut bounds = egui::Rect::from_min_size(t.pos, t.galley.size()); + if editor == Some(t.galley.text()) { + // Single-line TextEdit deliberately scrolls/clips its edit buffer; + // this exception applies only to the exact injected editor payload. + bounds = bounds.intersect(shape.clip_rect); + assert!(bounds.width() <= 64.0); + } + assert!( + panel.expand(0.5).contains_rect(bounds) + && shape.clip_rect.expand(0.5).contains_rect(bounds), + "clipped timeline text: {} at {bounds:?}, panel {panel:?}, clip {:?}", + t.galley.text(), + shape.clip_rect + ); + } + } +} +fn click( + ctx: &egui::Context, + app: &mut App, + selected: &PreviewFrame, + output: &egui::FullOutput, + label: &str, +) { + let pos = text_rect(output, label).center(); + for pressed in [true, false] { + let _ = frame( + ctx, + app, + 900.0, + Some(selected), + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + ); + } +} + +#[test] +fn timeline_empty_idle_uses_content_height_after_sidebar() { + for width in [900.0, 1250.0] { + let ctx = context(); + let mut app = App::default(); + let (output, rect) = frame(&ctx, &mut app, width, None, vec![]); + assert_visible(&output, rect); + for _ in 0..3 { + let _ = frame(&ctx, &mut app, width, None, vec![]); + } + let (output, rect) = frame(&ctx, &mut app, width, None, vec![]); + assert_visible(&output, rect); + eprintln!( + "idle timeline window={width}px strip={}px height={}px", + rect.width(), + rect.height() + ); + assert!(rect.left() >= 280.0); + assert!( + rect.height() <= 70.0, + "idle timeline {}px at window {width}", + rect.height() + ); + assert!(text_rect(&output, "▶ Play").is_positive()); + assert!(text_rect(&output, "Follow live").is_positive()); + assert!(app.follow_live && !app.playing); + } +} + +#[test] +fn timeline_controls_retain_transitions_and_physical_time() { + // Direct presentation data, not captured/generated numerical evidence. + let selected = PreviewFrame { + method: PressureImplementation::JacobiPcgV1, + step: 2, + time: 0.04, + seconds: 0.002, + edge: 1, + nonzero_tracer_cells: 0, + density: vec![], + report: None, + particle_report: None, + material_color: None, + }; + let ctx = context(); + let mut app = App::default(); + for width in [900.0, 1250.0] { + let cold = context(); + let mut cold_app = App::default(); + let (output, rect) = frame(&cold, &mut cold_app, width, Some(&selected), vec![]); + assert_visible(&output, rect); + } + let (output, rect) = frame(&ctx, &mut app, 900.0, None, vec![]); + assert_visible(&output, rect); + app.timeline_step = 2; + app.follow_live = false; + let (output, rect) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert_visible(&output, rect); // empty -> selected, without warm-up hiding clips + for _ in 0..3 { + let _ = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + } + let (output, rect) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert!(text_rect(&output, "Simulated 40.000 ms").is_positive()); + assert!(text_rect(&output, "Step calculation 2.000 ms").is_positive()); + for s in &output.shapes { + if let egui::Shape::Text(t) = &s.shape { + let bounds = egui::Rect::from_min_size(t.pos, t.galley.size()); + assert!( + rect.expand(0.5).contains_rect(bounds), + "caption overflow: {}", + t.galley.text() + ); + } + } + click(&ctx, &mut app, &selected, &output, "Step ▶"); + assert_eq!(app.timeline_step, 3); + assert!(!app.follow_live && !app.playing); + let (output, _) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "◀ Step"); + assert_eq!(app.timeline_step, 2); + let (output, _) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "▶ Play"); + assert!(app.playing && !app.follow_live); + assert_eq!(app.play_time.to_bits(), selected.time.to_bits()); + let (output, _) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "❚❚ Pause"); + assert!(!app.playing); + let (output, _) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "Follow live"); + assert!(app.follow_live && !app.playing); + assert_eq!(app.playback_speed, 1.0); +} + +// Pure values only. No Worker, history, mesh, file, process or numerical owner. +fn inert_carrier_request() -> Request { + Request { + collision_mesh: None, + size: 4, + steps: 4, + tight: false, + threads: 1, + methods: PressureImplementation::ALL.to_vec(), + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Adaptive, + domain_side: 1.0, + requested_dt: 0.01, + material: None, + material_color: [0; 3], + recording_limits: Default::default(), + recording_directory: String::new(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + } +} +fn presentation() -> PreviewFrame { + PreviewFrame { + method: PressureImplementation::JacobiPcgV1, + step: 2, + time: 0.04, + seconds: 0.002, + edge: 1, + nonzero_tracer_cells: 0, + density: vec![], + report: None, + particle_report: None, + material_color: None, + } +} +fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + [true, false] + .map(|pressed| egui::Event::Key { + key, + physical_key: None, + pressed, + repeat: false, + modifiers, + }) + .into() +} +fn replace_speed(ctx: &egui::Context, app: &mut App, selected: &PreviewFrame, text: &str) { + let _ = frame( + ctx, + app, + 900.0, + Some(selected), + key( + egui::Key::A, + egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }, + ), + ); + let _ = frame( + ctx, + app, + 900.0, + Some(selected), + vec![egui::Event::Text(text.into())], + ); +} + +#[test] +fn timeline_speed_edit_focus_bounds_validation_and_keyboard() { + let ctx = context(); + let selected = presentation(); + let mut app = App::default(); + app.running_request = Some(inert_carrier_request()); + app.playback_speed = 1.2345678901234567; + app.playback_speed_text = app.playback_speed.to_string(); + let before = app.playback_speed.to_bits(); + let (output, rect) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert_visible_with_editor(&output, rect, Some(&app.playback_speed_text)); + click(&ctx, &mut app, &selected, &output, "1.2345678901234567"); + let (focused, panel) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert_visible_with_editor(&focused, panel, Some(&app.playback_speed_text)); + let tree = focused.platform_output.accesskit_update.as_ref().unwrap(); + let node = &tree + .nodes + .iter() + .find(|(id, _)| *id == tree.focus) + .unwrap() + .1; + assert_eq!(node.role(), egui::accesskit::Role::TextInput); + assert!(!node.labelled_by().is_empty()); + assert_eq!(app.playback_speed.to_bits(), before); + for _ in 0..2 { + let _ = frame( + &ctx, + &mut app, + 900.0, + Some(&selected), + key(egui::Key::Tab, Default::default()), + ); + } + assert_eq!(app.playback_speed.to_bits(), before); + let (output, _) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "1.2345678901234567"); + replace_speed(&ctx, &mut app, &selected, "2.5"); + assert_eq!(app.playback_speed, 2.5); + let _ = frame( + &ctx, + &mut app, + 900.0, + Some(&selected), + key(egui::Key::ArrowUp, Default::default()), + ); + assert_eq!(app.playback_speed, 2.5 + 0.1); + let kept = app.playback_speed.to_bits(); + for text in ["NaN".to_owned(), "x".repeat(4096)] { + replace_speed(&ctx, &mut app, &selected, &text); + assert_eq!(app.playback_speed.to_bits(), kept); + assert!(app.playback_speed_invalid); + let (output, rect) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert_visible_with_editor(&output, rect, Some(&text)); + assert!(text_rect(&output, "Invalid speed").is_positive()); + } + for (input, expected) in [("9", 4.0), ("0.01", 0.1), ("1.5", 1.5)] { + replace_speed(&ctx, &mut app, &selected, input); + assert_eq!(app.playback_speed, expected); + assert!(!app.playback_speed_invalid); + } + let (output, rect) = frame(&ctx, &mut app, 900.0, Some(&selected), vec![]); + assert_visible(&output, rect); +} + +#[test] +fn timeline_track_selection_and_report_rows_fit_cold_and_changed_states() { + for width in [900.0, 1250.0] { + let ctx = context(); + let mut app = App::default(); + app.running_request = Some(inert_carrier_request()); + let mut selected = presentation(); + selected.report = Some(rheon::StepReport { + dt: 0.01, + time: 0.04, + generation: 2, + pressure: rheon::PressureReport { + iterations: usize::MAX, + true_residual_l2: 0.0, + true_residual_max: 0.0, + predicted_divergence_max: 0.0, + }, + actual_divergence_max: 1.25e-5, + courant: 0.2, + tracer_integral: 0.0, + kinetic_energy: 0.0, + }); + selected.particle_report = Some(rheon::ParticleStepReport { + dt: 0.01, + particles: usize::MAX, + emitted_mass: 0.0, + total_mass: 1.234567, + max_speed: 0.0, + max_pressure: 0.0, + mean_temperature_kelvin: 293.15, + }); + let (output, rect) = frame(&ctx, &mut app, width, Some(&selected), vec![]); + assert_visible(&output, rect); + assert!( + text_rect( + &output, + &format!("{} particles · 1.234567 kg · 20.0 °C", usize::MAX) + ) + .is_positive() + ); + // No popup geometry is compared with the panel; popups are separate layers. + if width == 900.0 { + click(&ctx, &mut app, &selected, &output, "jacobi-pcg-v1"); + let (output, _) = frame(&ctx, &mut app, width, Some(&selected), vec![]); + click(&ctx, &mut app, &selected, &output, "sgs-pcg-v1"); + assert_eq!(app.track, 1); + assert!(!app.follow_live && !app.playing); + assert_eq!(app.timeline_step, 0); + } + let (output, rect) = frame(&ctx, &mut app, width, Some(&selected), vec![]); + assert_visible(&output, rect); + selected.report = None; + selected.particle_report = None; + let (output, rect) = frame(&ctx, &mut app, width, Some(&selected), vec![]); + assert_visible(&output, rect); + } +} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index 7b0c9288..dceb1fed 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -57,6 +57,9 @@ mod settings; #[cfg(test)] #[path = "desktop/setup_ui_tests.rs"] mod setup_ui_tests; +#[cfg(test)] +#[path = "desktop/timeline_ui_tests.rs"] +mod timeline_ui_tests; #[path = "desktop/transport_view.rs"] mod transport_view; #[path = "desktop/viewport.rs"] @@ -1063,6 +1066,180 @@ fn running_setup_ui(ui: &mut egui::Ui, request: &Request) { ui.small("Filled carrier-fluid preview"); } } +/// Per-paint value projection only; App remains the playback state owner. +#[derive(Clone)] +struct TimelineControls { + timeline_step: usize, + track: usize, + follow_live: bool, + playing: bool, + play_time: f64, + playback_speed: f64, + playback_speed_text: String, + playback_speed_invalid: bool, +} +impl TimelineControls { + fn contents( + &mut self, + ui: &mut egui::Ui, + count: usize, + frame: Option<&PreviewFrame>, + request: Option<&Request>, + track_caption: &str, + ) { + ui.horizontal_wrapped(|ui| { + if ui + .button(if self.playing { + "❚❚ Pause" + } else { + "▶ Play" + }) + .clicked() + { + self.follow_live = false; + self.playing = !self.playing; + if self.timeline_step + 1 >= count { + self.timeline_step = 0; + self.play_time = 0.0; + } else { + self.play_time = frame.as_ref().map_or(0.0, |f| f.time); + } + } + if ui.button("◀ Step").clicked() { + self.follow_live = false; + self.playing = false; + self.timeline_step = self.timeline_step.saturating_sub(1); + } + if ui.button("Step ▶").clicked() { + self.follow_live = false; + self.playing = false; + self.timeline_step = (self.timeline_step + 1).min(count.saturating_sub(1)); + } + ui.checkbox(&mut self.follow_live, "Follow live"); + if self.follow_live { + self.playing = false; + } + // A bounded editor has the same geometry while disabled, focused or + // holding invalid text. DragValue's growing private edit buffer does not. + let speed_label = ui.label("Speed"); + let speed = ui + .add( + egui::TextEdit::singleline(&mut self.playback_speed_text) + .id_salt("playback-speed") + .desired_width(44.0) + .clip_text(true), + ) + .labelled_by(speed_label.id) + .on_hover_text( + "Playback speed: 0.1–4×. Edit the number; Up/Down changes it by 0.1.", + ); + ui.label("×"); + // Feedback is projected from the start of this paint, so input cannot + // add an unmeasured row partway through bottom-panel placement. + let displayed_invalid = self.playback_speed_invalid; + if displayed_invalid { + ui.colored_label(egui::Color32::LIGHT_RED, "Invalid speed") + .on_hover_text("Enter a finite number. Allowed range: 0.1–4×."); + } + if speed.changed() { + match self.playback_speed_text.parse::() { + Ok(value) if value.is_finite() => { + self.playback_speed = value.clamp(0.1, 4.0); + self.playback_speed_invalid = false; + } + _ => self.playback_speed_invalid = true, + } + } + if speed.has_focus() { + let increment = ui.input(|input| { + i32::from(input.key_pressed(egui::Key::ArrowUp)) + - i32::from(input.key_pressed(egui::Key::ArrowDown)) + }); + if increment != 0 { + self.playback_speed = + (self.playback_speed + f64::from(increment) * 0.1).clamp(0.1, 4.0); + self.playback_speed_text = self.playback_speed.to_string(); + self.playback_speed_invalid = false; + } + } else if !self.playback_speed_invalid { + self.playback_speed_text = self.playback_speed.to_string(); + } + if let Some(request) = request + && request.material.is_none() + { + egui::ComboBox::from_id_salt("timeline-track") + .selected_text(request.track_label(self.track)) + .show_ui(ui, |ui| { + for track in 0..request.methods.len() { + if ui + .selectable_value( + &mut self.track, + track, + request.track_label(track), + ) + .clicked() + { + self.follow_live = false; + self.playing = false; + self.timeline_step = 0; + } + } + }); + } + }); + ui.style_mut().spacing.slider_width = (ui.available_width() - 155.0).max(150.0); + if ui + .add_enabled( + count > 0, + egui::Slider::new(&mut self.timeline_step, 0..=count.saturating_sub(1)) + .text("Step"), + ) + .changed() + { + self.follow_live = false; + self.playing = false; + } + if let Some(frame) = &frame { + ui.horizontal_wrapped(|ui| { + ui.label(format!( + "{} · Step {} / {}", + if frame.material_color.is_some() { + "Material fluid".to_owned() + } else { + track_caption.to_owned() + }, + frame.step, + count.saturating_sub(1) + )); + ui.separator(); + ui.label(format!("Simulated {}", settings::duration(frame.time))); + ui.separator(); + ui.label(if frame.step == 0 { + "Initial state".into() + } else { + format!("Step calculation {}", settings::duration(frame.seconds)) + }); + if let Some(r) = frame.particle_report { + ui.separator(); + ui.label(format!( + "{} particles · {:.6} kg · {:.1} °C", + r.particles, + r.total_mass, + r.mean_temperature_kelvin - 273.15 + )); + } + if let Some(report) = frame.report { + ui.separator(); + ui.label(format!( + "{} pressure iterations · divergence {:.2e}", + report.pressure.iterations, report.actual_divergence_max + )); + } + }); + } + } +} + struct App { setup_workflow: SetupWorkflow, ai_comparison: Option, @@ -1111,6 +1288,8 @@ struct App { playing: bool, play_time: f64, playback_speed: f64, + playback_speed_text: String, + playback_speed_invalid: bool, } impl Default for App { fn default() -> Self { @@ -1166,6 +1345,8 @@ impl Default for App { playing: false, play_time: 0.0, playback_speed: 1.0, + playback_speed_text: "1".into(), + playback_speed_invalid: false, } } } @@ -1819,6 +2000,66 @@ impl App { } } } + /// Paint playback only; frame/history/worker ownership stays with the caller. + fn timeline_ui( + &mut self, + ctx: &egui::Context, + count: usize, + frame: Option<&PreviewFrame>, + ) -> egui::Rect { + let mut controls = TimelineControls { + timeline_step: self.timeline_step, + track: self.track, + follow_live: self.follow_live, + playing: self.playing, + play_time: self.play_time, + playback_speed: self.playback_speed, + playback_speed_text: self.playback_speed_text.clone(), + playback_speed_invalid: self.playback_speed_invalid, + }; + let request = self.running_request.as_ref(); + // Freeze display context across sizing and interactive painting. A changed + // track is projected on the next frame, without measuring mutable history. + let track_caption = request.map_or_else( + || frame.map_or_else(String::new, |frame| frame.method.id().to_owned()), + |request| request.track_label(controls.track), + ); + let panel_frame = egui::Frame::side_top_panel(&ctx.style()); + let mut sizing_ui = egui::Ui::new( + ctx.clone(), + egui::Id::new("timeline-sizing"), + egui::UiBuilder::new() + .max_rect(ctx.available_rect()) + .layout(egui::Layout::top_down(egui::Align::Min)) + .invisible(), + ); + // Invisible widgets still produce AccessKit nodes. Hide the measurement + // subtree so assistive technology receives only the real playback controls. + ctx.accesskit_node_builder(sizing_ui.id(), |node| node.set_hidden()); + // Bottom panels reserve height before painting. Measure the same contents + // locally so cold frames and changing captions never rely on cached height. + // This disabled, disjoint-ID pass edits only a disposable scalar copy. + let mut sizing_controls = controls.clone(); + let measured = panel_frame.show(&mut sizing_ui, |ui| { + sizing_controls.contents(ui, count, frame, request, &track_caption); + }); + let response = egui::TopBottomPanel::bottom("timeline") + .frame(panel_frame) + .exact_height(measured.response.rect.height().ceil()) + .show(ctx, |ui| { + controls.contents(ui, count, frame, request, &track_caption); + }); + // Commit real interaction once. Never rerun App::update to measure layout. + self.timeline_step = controls.timeline_step; + self.track = controls.track; + self.follow_live = controls.follow_live; + self.playing = controls.playing; + self.play_time = controls.play_time; + self.playback_speed = controls.playback_speed; + self.playback_speed_text = controls.playback_speed_text; + self.playback_speed_invalid = controls.playback_speed_invalid; + response.response.rect + } fn controls(&mut self, ui: &mut egui::Ui) { let configured = self.setup_workflow == SetupWorkflow::AcceptedSession; let material_preview = self.material_mode && !configured; @@ -2141,124 +2382,7 @@ impl eframe::App for App { ctx.request_repaint(); return; } - egui::TopBottomPanel::bottom("timeline") - .min_height(106.0) - .show(ctx, |ui| { - ui.horizontal_wrapped(|ui| { - if ui - .button(if self.playing { - "❚❚ Pause" - } else { - "▶ Play" - }) - .clicked() - { - self.follow_live = false; - self.playing = !self.playing; - if self.timeline_step + 1 >= count { - self.timeline_step = 0; - self.play_time = 0.0; - } else { - self.play_time = frame.as_ref().map_or(0.0, |f| f.time); - } - } - if ui.button("◀ Step").clicked() { - self.follow_live = false; - self.playing = false; - self.timeline_step = self.timeline_step.saturating_sub(1); - } - if ui.button("Step ▶").clicked() { - self.follow_live = false; - self.playing = false; - self.timeline_step = (self.timeline_step + 1).min(count.saturating_sub(1)); - } - ui.checkbox(&mut self.follow_live, "Follow live"); - if self.follow_live { - self.playing = false; - } - ui.add( - egui::DragValue::new(&mut self.playback_speed) - .speed(0.1) - .range(0.1..=4.0) - .suffix("× playback"), - ); - if let Some(request) = &self.running_request - && request.material.is_none() - { - egui::ComboBox::from_id_salt("timeline-track") - .selected_text(request.track_label(self.track)) - .show_ui(ui, |ui| { - for track in 0..request.methods.len() { - if ui - .selectable_value( - &mut self.track, - track, - request.track_label(track), - ) - .clicked() - { - self.follow_live = false; - self.playing = false; - self.timeline_step = 0; - } - } - }); - } - }); - ui.style_mut().spacing.slider_width = (ui.available_width() - 155.0).max(150.0); - if ui - .add_enabled( - count > 0, - egui::Slider::new(&mut self.timeline_step, 0..=count.saturating_sub(1)) - .text("Step"), - ) - .changed() - { - self.follow_live = false; - self.playing = false; - } - if let Some(frame) = &frame { - ui.horizontal_wrapped(|ui| { - ui.label(format!( - "{} · Step {} / {}", - if frame.material_color.is_some() { - "Material fluid".to_owned() - } else { - self.running_request.as_ref().map_or_else( - || frame.method.id().to_owned(), - |request| request.track_label(self.track), - ) - }, - frame.step, - count.saturating_sub(1) - )); - ui.separator(); - ui.label(format!("Simulated {}", settings::duration(frame.time))); - ui.separator(); - ui.label(if frame.step == 0 { - "Initial state".into() - } else { - format!("Step calculation {}", settings::duration(frame.seconds)) - }); - if let Some(r) = frame.particle_report { - ui.separator(); - ui.label(format!( - "{} particles · {:.6} kg · {:.1} °C", - r.particles, - r.total_mass, - r.mean_temperature_kelvin - 273.15 - )); - } - if let Some(report) = frame.report { - ui.separator(); - ui.label(format!( - "{} pressure iterations · divergence {:.2e}", - report.pressure.iterations, report.actual_divergence_max - )); - } - }); - } - }); + self.timeline_ui(ctx, count, frame.as_deref()); let workers = if self.follow_live && self.show_workers { self.worker .as_ref() From 6127cecbdcfe53270347e59ebe86ff3317528cfe Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 23:05:37 +0000 Subject: [PATCH 09/25] feat(desktop): author canonical forces in a right scene inspector Use one ForceScene, checked shared model/force IDs and context-scoped selection for directional, radial and vortex authoring. Preserve validated SI properties, immutable retained geometry and playback index; narrow windows reuse the existing run/status owner. Refuse new authored-force runs and independent unsupported labs/AI entry points until runtime and archive integration exists. Fourteen individually inspected exact UI-only tests, locked check, strict Clippy and independent source/evidence review pass. Native rendering, viewport picking/outlines/handles and authored runtime remain explicit follow-up scope; no numerical or model workloads executed. --- .../scene-authoring-20261020/.gitattributes | 1 + .../scene-authoring-20261020/check.log | 2 + .../scene-authoring-20261020/clippy.log | 2 + .../scene-authoring-20261020/context.log | 8 + .../scene-authoring-20261020/edits.log | 8 + .../scene-authoring-20261020/enumeration.log | 182 +++++ .../scene-authoring-20261020/format.log | 0 .../evidence/scene-authoring-20261020/ids.log | 8 + .../scene-authoring-20261020/manifest.json | 132 ++++ .../scene-authoring-20261020/menus.log | 8 + .../pointer-before.log | 18 + .../scene-authoring-20261020/pointer.log | 9 + .../scene-authoring-20261020/precision.log | 8 + .../scene-authoring-20261020/refusal.log | 8 + .../scene-authoring-20261020/selection.log | 8 + .../scene-authoring-20261020/thermal.log | 8 + .../timeline-controls.log | 8 + .../timeline-idle.log | 10 + .../timeline-speed.log | 8 + .../timeline-track.log | 8 + .../scene-authoring-20261020/validation.log | 8 + .../scene-authoring-20261020/whitespace.log | 0 docs/plans/ui-refinement/execution-ledger.md | 39 + docs/plans/ui-refinement/plan.md | 21 +- .../reports/native-scene-authoring-handoff.md | 73 ++ .../reports/scene-authoring-admission.md | 5 +- .../reports/scene-authoring-source-review.md | 235 ++++++ src/bin/desktop/scene_ui.rs | 713 ++++++++++++++++++ src/bin/desktop/settings.rs | 3 +- src/bin/desktop/viewport.rs | 10 +- src/bin/rheon-desktop.rs | 147 +++- 31 files changed, 1663 insertions(+), 35 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/check.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/clippy.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/context.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/edits.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/enumeration.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/format.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/ids.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/menus.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer-before.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/precision.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/refusal.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/selection.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/thermal.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-controls.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-idle.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-speed.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-track.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/validation.log create mode 100644 docs/plans/ui-refinement/evidence/scene-authoring-20261020/whitespace.log create mode 100644 docs/plans/ui-refinement/reports/native-scene-authoring-handoff.md create mode 100644 docs/plans/ui-refinement/reports/scene-authoring-source-review.md create mode 100644 src/bin/desktop/scene_ui.rs diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/.gitattributes b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/check.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/check.log new file mode 100644 index 00000000..fb7dc5ef --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.37s diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/clippy.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/clippy.log new file mode 100644 index 00000000..af1e7071 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 52.99s diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/context.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/context.log new file mode 100644 index 00000000..a6ef3292 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/context.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/edits.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/edits.log new file mode 100644 index 00000000..d9144cb9 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/edits.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.09s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/enumeration.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/enumeration.log new file mode 100644 index 00000000..5335ec9e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/enumeration.log @@ -0,0 +1,182 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 8.83s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +177 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/format.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/format.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/ids.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/ids.log new file mode 100644 index 00000000..6986ac25 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/ids.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/manifest.json b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/manifest.json new file mode 100644 index 00000000..f55af019 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/manifest.json @@ -0,0 +1,132 @@ +{ + "canonical_backend": "43ec505218c43594641ecbb80873bec8c88d7d2c", + "tested_prerequisite_commit": "7dcbfa67578875dd266020be589e2fbea40467f8", + "source_sha256": { + "src/bin/rheon-desktop.rs": "32be43dd2ce112e30017b6b9bf706607f942ca574b2415f74237c296f79614c5", + "src/bin/desktop/scene_ui.rs": "68d3eed0bdfd1ee424854325349d3e27391cbdf85b352f4c7acb5a0c7d5a11ec", + "src/bin/desktop/settings.rs": "72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4", + "src/bin/desktop/viewport.rs": "a2ae713e3a06f2370e72300593d208c1991aa201b41e0924cd87268a48a296e1", + "src/bin/desktop/timeline_ui_tests.rs": "653848ce12986065b966c6ab2d97b23c2e7da4cdf5ec7f5a6b99c1e5a9ef2c35" + }, + "exact_test_names": [ + "scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values", + "scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context", + "scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation", + "scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch", + "scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history", + "timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar", + "timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time", + "timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard", + "timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states", + "settings::tests::ui_precision_tab_and_focus_preserve_material_bits", + "settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges", + "settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing", + "settings::tests::ui_material_selection_updates_only_its_existing_owner", + "settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms" + ], + "logs": { + "pointer": { + "sha256": "821921e701e81a883b6d6d050c3b70c7b347f94b39f101d04ef73c4cae6de08d", + "bytes": 453, + "source": "/tmp/rheon-ui-scene-authoring-exact-pointer-2.log" + }, + "ids": { + "sha256": "0d14716463bb12e599807213cedfc1b54401f90c50f95c7b98228137f6b91411", + "bytes": 411, + "source": "/tmp/rheon-ui-scene-authoring-final-ids.log" + }, + "validation": { + "sha256": "1ebc6a4e67a968a4340955537b3596a52a87e770244d126fa0ab46684a8aa1f5", + "bytes": 412, + "source": "/tmp/rheon-ui-scene-authoring-final-validation.log" + }, + "refusal": { + "sha256": "9a97cd70ad8d5a54eec23af8f54216cac9a9c409ea38c9f6197e9d003069c8bf", + "bytes": 398, + "source": "/tmp/rheon-ui-scene-authoring-final-refusal.log" + }, + "context": { + "sha256": "f788e61f78e06cf66b593de9e4f3b8251f4a25eb2412896453eb2909312e8e05", + "bytes": 411, + "source": "/tmp/rheon-ui-scene-authoring-final-context.log" + }, + "precision": { + "sha256": "356a777c5a9fa7b542b84a2d39112856bcc00ae8af85f17eaf45f011a66e2fc0", + "bytes": 389, + "source": "/tmp/rheon-ui-scene-authoring-final-precision.log" + }, + "edits": { + "sha256": "6169ce8fe341d66cd7f3d2b2f06020c2d0a41824128f24a9f09c1066117b99c3", + "bytes": 392, + "source": "/tmp/rheon-ui-scene-authoring-final-edits.log" + }, + "thermal": { + "sha256": "419a69dd6b2943363028f96fa91758c6d937d3b24c40c2ce9b928b5b1943e89f", + "bytes": 402, + "source": "/tmp/rheon-ui-scene-authoring-final-thermal.log" + }, + "selection": { + "sha256": "5e5b8709c44f77d0f16aefc66f8da1a7df88adeb00e3ded63e08217d82f7c022", + "bytes": 393, + "source": "/tmp/rheon-ui-scene-authoring-final-selection.log" + }, + "menus": { + "sha256": "b6b84be6f1ab267bdd9605da885114ac6df523688df677b4147e5741cdcfe58b", + "bytes": 391, + "source": "/tmp/rheon-ui-scene-authoring-final-menus.log" + }, + "timeline-idle": { + "sha256": "36b73dccd613ba28d16013236e34b8fcbf9cce70bf74356fc33e47d5a44b0977", + "bytes": 498, + "source": "/tmp/rheon-ui-scene-authoring-final-timeline-idle.log" + }, + "timeline-controls": { + "sha256": "86d14aa0580ec48466caad6d49a69943bbce8648c4d95948d3f86e82a6780f08", + "bytes": 396, + "source": "/tmp/rheon-ui-scene-authoring-final-timeline-controls.log" + }, + "timeline-speed": { + "sha256": "6173f273cee320d54647b8cb49e47aeff2fbd4ec4060058faa45adcfc17aeb65", + "bytes": 398, + "source": "/tmp/rheon-ui-scene-authoring-final-timeline-speed.log" + }, + "timeline-track": { + "sha256": "ebc7c05145ec6185915c6742a2a91748f2052596915f5e3298965b162c0bfa89", + "bytes": 410, + "source": "/tmp/rheon-ui-scene-authoring-final-timeline-track.log" + }, + "clippy": { + "sha256": "46564fd1dedbb64a09a3b802bfee55ef24c133b74cde342563e57da1a1d4c524", + "bytes": 131, + "source": "/tmp/rheon-ui-scene-authoring-clippy-1.log" + }, + "check": { + "sha256": "74a7cd182488c042c3d146e06986c705100893aa8f197c1c0e493b11aa5cc5b1", + "bytes": 130, + "source": "/tmp/rheon-ui-scene-authoring-final-check.log" + }, + "format": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-scene-authoring-final-fmt.log" + }, + "whitespace": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-scene-authoring-final-diff-check.log" + }, + "enumeration": { + "sha256": "18ab1a20078e6662d8a64b9f3106b8f115034a00c76e8dff080643f1d3af13a6", + "bytes": 18867, + "source": "/tmp/rheon-ui-scene-authoring-test-list-5.log" + }, + "pointer-before": { + "sha256": "cb05b62162a22a422856227116b8a0ee16ca57044bf31aadbbe5e17744dbadf0", + "bytes": 811, + "source": "/tmp/rheon-ui-scene-authoring-exact-pointer-1.log" + } + }, + "execution_scope": "14 individually named inspected UI-only tests; no solver/model/worker/history/filesystem fixture/cleanup", + "limitations": "No native/screenshots, actual mesh rollback, active worker/progress/Cancel, viewport picking/outline/handles or authored runtime/archive acceptance", + "historical_incident_log_sha256": "9d1b7245761f461a86dc436f21911f4a5525af3d2b8f7692d6ff8b5e6f2bbba1" +} diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/menus.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/menus.log new file mode 100644 index 00000000..e805be77 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/menus.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.42s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer-before.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer-before.log new file mode 100644 index 00000000..bffddc77 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer-before.log @@ -0,0 +1,18 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test + +thread 'scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context' (39493) panicked at src/bin/desktop/scene_ui.rs:388:28: +missing visible 0 N/m³ +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +test scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context ... FAILED + +failures: + +failures: + scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.05s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer.log new file mode 100644 index 00000000..2f49077d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/pointer.log @@ -0,0 +1,9 @@ + Blocking waiting for file lock on build directory + Finished `test` profile [unoptimized + debuginfo] target(s) in 4.08s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.11s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/precision.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/precision.log new file mode 100644 index 00000000..ec74a426 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/precision.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test settings::tests::ui_precision_tab_and_focus_preserve_material_bits ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 1.12s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/refusal.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/refusal.log new file mode 100644 index 00000000..b90f671a --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/refusal.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/selection.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/selection.log new file mode 100644 index 00000000..fdefe292 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/selection.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test settings::tests::ui_material_selection_updates_only_its_existing_owner ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.09s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/thermal.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/thermal.log new file mode 100644 index 00000000..fb026844 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/thermal.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.21s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.05s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-controls.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-controls.log new file mode 100644 index 00000000..bbcbc62d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-controls.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.03s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-idle.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-idle.log new file mode 100644 index 00000000..f1812e62 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-idle.log @@ -0,0 +1,10 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +idle timeline window=900px strip=620px height=48px +idle timeline window=1250px strip=970px height=48px +test timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-speed.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-speed.log new file mode 100644 index 00000000..3363f022 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-speed.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.04s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-track.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-track.log new file mode 100644 index 00000000..09283416 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/timeline-track.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.02s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/validation.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/validation.log new file mode 100644 index 00000000..41f62cc5 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/validation.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.22s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 176 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/scene-authoring-20261020/whitespace.log b/docs/plans/ui-refinement/evidence/scene-authoring-20261020/whitespace.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index a6f85768..15ccfd24 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -300,3 +300,42 @@ - Next admission covers canonical right scene list/properties/force creation, immutable context selection and explicit launch refusal; viewport picking and handles follow in separate bounded slices. No force runtime dispatch yet. + + +## 2026-10-20 — canonical scene authoring checkpoint + +- Normal published implementation branch now derives from canonical backend43ec5052. + Prerequisite UI commits were cherry-picked with -x, preserving original branches, + M2a worktree and incident logs. Distinct current commits:676d8a25 M2a preservation, + 4604182a precision,7dcbfa67 completed timeline. Draft PR146 bases on backend PR145; + no force push/merge/deletion. Backend core unchanged from sealed1d44/3e839. +- SceneUi owns one ForceScene, checked model/force SceneObjectId allocation and + context-scoped selection. Right drawer creates/selects Directional/Radial/Vortex, + edits unit-labelled validated properties and shows blocking errors. Names, + enabled state, support/falloff, times, translation/orientation retain canonical + meanings. New strengths zero; existing gravity remains additive. +- Draft/Retained run contexts use their actual geometry and separate selected state. + Fixed review findings: retained Inspector filtered from Draft, stale selection + cleared on every new run, invalid draft error scoped to Draft, and Draft timeline + omitted so disabled-slider clamping cannot reset retained index. Pure roundtrip + regression preserves index7/track1/time1.25 without any real history. +- Import/replace ID admission is transactional: failure restores previous accepted + mesh and preserves history/selection. Existing CLI import also admits model ID. + Model properties currently show accepted geometry; placement remains transactional + through setup. No second canonical force/model property store. +- Nonempty authored drafts, including disabled objects, refuse new preview/configured + runs before path/output creation. Independent lab/AI workflows are disabled in + authoring context; legacy frozen reruns keep their own original intent. No force + runtime, recorder/desktop frozen intent, viewport glyph/pick/outline or handle + implementation is claimed by this source slice. +- Five exact inspected scene tests pass, plus all five exact precision and four + exact timeline probes on the combined source. List-only177 enumeration preceded + each allowlisted execution; no substring/broad fallback. Pointer1 lookup-only + failure retained; numeric+unit lookup fixes format-dependent test targeting. + Strict Clippy52.99s passed; current check/fmt/diff logs and source review recorded. + No numerical/model/Worker/history/IO fixture/cleanup workload was executed. +- Native display remains unavailable. Handoff specifies existing idle fixture only, + two sizes/scales, quality85 actual JPEGs and explicit unsupported-state coverage. + Active progress/Cancel, model rollback/native, actual viewport mode guard and + whole-app focus/AT behavior remain unverified. Prior incident teardown/scientific + evidence limits and parked live cancellation are unchanged. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 2b673e4b..302c6eb1 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T1 source complete; T2 canonical scene authoring admission -**Next slice:** canonical right scene list and validated properties +**Current phase:** T2 authoring source gates complete; native validation pending +**Next slice:** T3 viewport picking and selected-object outline **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -14,7 +14,9 @@ [UI-5 precision repair](reports/ui5-precision-repair.md) · [T1 compact checkpoint](reports/timeline-compact-checkpoint.md) · [Backend receipt](reports/backend-integration-receipt.md) · -[Current authoring admission](reports/scene-authoring-admission.md) +[Current authoring admission](reports/scene-authoring-admission.md) · +[Authoring source review](reports/scene-authoring-source-review.md) · +[Native authoring handoff](reports/native-scene-authoring-handoff.md) ## Objective @@ -38,7 +40,12 @@ No optional approval is needed for the reversible T1 slice after plan review. ## Source, standards and preservation -Local HEAD remains7318023f04946171d7909119300af76e455c2926. The original uncommitted +Historical preserved baseline is7318023f04946171d7909119300af76e455c2926. +Active worktree is /workspace/Rheon-ui-authoring, branch +implementation/scene-authoring-ui-20261020, based on canonical backend43ec5052. +The five earlier UI commits and distinct M2a/precision/T1 commits were cherry-picked +with explicit -x provenance, preserving original branches and every checkpoint. +Draft PR146 is based on PR145; no merge or force push. The original uncommitted M2a worktree `/workspace/Rheon-ui-hierarchy`, its exact patch and logs are untouched. Detached `/workspace/Rheon-ui-precision` applies that M2a overlay then UI-5; the reviewed precision checkpoint is frozen as tree3e1fd57146e2f1a02abe22a09ac317497f39d738 @@ -86,7 +93,7 @@ Inspector slice before any overlapping changes; T1 excludes Inspector entirely. | Milestone | Exact production write set and outcome | Dependency / gate | State | | --- | --- | --- | --- | | T1 | main timeline presentation and desktop/timeline_ui_tests.rs; bounded labelled speed editor, ordinary invisible local measurement, unchanged physical context | Four inspected exact UI probes + compile/Clippy/fmt + independent source review; native remains separate | Completed (bounded source) | -| T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; no viewport/runtime changes | Active | +| T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests; viewport.rs mode-control guard only (no lab dispatch/picking changes) | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; viewport mode refusal only, no runtime dispatch | Completed (bounded source) | | T3 | viewport.rs picking and thin actual-geometry outline; scene_ui.rs same selection API; main projection routing | T2 source gates, reviewed exclusive pick/camera/probe policy; inert projection tests then native | Planned | | T4 | desktop/scene_manipulator.rs, viewport gesture routing, scene_ui validated transform intent and collision_ui transactional model placement | Same IDs/domain capabilities; no duplicate force physics; Escape rollback, explicit model Apply | Planned | | T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | @@ -138,8 +145,8 @@ Generated logs/screenshots/transfer packets remain outside Git. | ID | Observable criterion | Kind / environment / mode | Status | Evidence | | --- | --- | --- | --- | --- | | S1 | Empty idle timeline fits <=70 logical px in900/1250px windows with the preceding280px sidebar reserved; selected-frame captions remain present and may naturally wrap/grow; actual Play/Pause/step/Live input keeps prior transitions | integration / isolated egui painting/input / automated | pending | Four exact probes pass: idle48px versus106px, focused speed editing and native-track/literal-report layouts. Native/configured/popup-keyboard coverage remains open | -| S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | No scene implementation yet | -| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Exact canonical source received/sealed; desktop dispatch/frozen intent integration pending | +| S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | Canonical authoring source and five exact UI probes pass; viewport picking/native remain pending | +| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent still pending | | S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | Domain/manipulator stages not admitted yet | | S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; prior426fd25 images do not cover this source | | S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T1 focused gates reported in checkpoint; no scientific certificate | diff --git a/docs/plans/ui-refinement/reports/native-scene-authoring-handoff.md b/docs/plans/ui-refinement/reports/native-scene-authoring-handoff.md new file mode 100644 index 00000000..d81f37f1 --- /dev/null +++ b/docs/plans/ui-refinement/reports/native-scene-authoring-handoff.md @@ -0,0 +1,73 @@ +# Native handoff: compact timeline and canonical scene authoring + +Use the exact tested PR146 commit supplied in the checkpoint receipt, not a moving +branch without hash verification. Dependency is canonical backend43ec505218c43594641ecbb80873bec8c88d7d2c; +core source matches the independently sealed1d44/3e839 custody implementation. +Build the desktop target with locked existing dependencies. Record source/tree, +binary hash, build result and actual window/scale for each observation. + +This source slice provides a collapsible right Scene list, canonical force creation +and selected properties. It does NOT provide force viewport glyphs/picking/outline, +handles, force runtime dispatch, force archive/rerun intent or end-to-end solver +parity. All nonempty authored drafts, even disabled/zero objects, refuse new runs. +Existing gravity and configured-only forces are separate legacy inputs; the new +list is explicitly Authored forces. Do not call it all/no forces. + +## Idle procedure: no numerical/model execution + +1. Launch normally without --collision-run or any automatic workload option. At + 1250×820 and900×650,100% and150%, open Scene. At narrow logical width the setup + drawer gives way to Scene, retaining the same top run/status/Cancel owner. + Hide/reopen Scene; verify setup values and selection survive and viewport stays + usable. Status details must remain reachable. Do not start a simulation. +2. Add Directional, Radial and Vortex via Add force. Check immediate selection, + selected properties/name, units m/s² versus N/m³, direction/signed strength, + metre positions, degree orientation, Global/Sphere/Box support/falloff and + [start,end) seconds. Rename and edit; ensure values persist when switching rows. + Unit selection deliberately reinterprets authored numbers; it does not invent + an acceleration-to-density conversion. Gravity stays additive. +3. Set radius0 or invalid times, confirm visible validation. Correct them. Toggle + Enabled, remove objects, and verify selection is cleared on deletion without + another object becoming its edit target. With any authored object present, + verify Run and independent lab/AI launch affordances are disabled with a reason. + No authored-force numerical execution is part of this procedure. +4. Retained/Draft switching requires only an existing separately approved immutable + retained fixture. Otherwise mark it unavailable. Do not create a run to test it. + Draft must hide retained fluid/particle probes and preserve history/index; + Retained must use frozen geometry and show unavailable authored-force metadata + rather than the current draft. Check selected step/track remains unchanged. +5. Likewise model import/replacement can use an existing separately approved idle + mesh fixture only. Otherwise report this native gap. No mesh/recording file + modifications or solver run. Check accepted model row/properties, replacement + clearing and original recording files unchanged. ID exhaustion is pure-test + covered, not a reason to launch/import repeated fixtures. +6. For timeline playback use an existing separately approved retained fixture; + never generate one by running a solver. Check bounded speed focus/edit, invalid + text feedback,0.1–4× clamp, Up/Down, track popup, step/scrub and report wrapping. + Source probes cover literal UI states; they do not replace this native check. + +Active progress/Cancel/join/frozen rerun remain separately parked/unverified; this +handoff does not authorize a live cancellation workload or provision containment. +Do not click Obstacle/Transport/AI work around the explicit authored-force refusal. +If an unexpected workload starts, stop the procedure and preserve evidence; no +retry/tuning/cleanup or numerical acceptance is authorized here. + +## JPEG evidence + +Capture actual updated rendered screens after inspection, with clear state labels. +Export JPEG quality85 unless text readability requires adjustment; retain exact +source revision/tree/state/window/scale and confirmed Library attachment identity. +Suggested states: Scene collapsed, three-object list and selected properties, +expanded transform/support, validation error, and narrow drawer with visible run +status. Keep historical screenshots and original archive/fixture bytes intact. +No screenshot/native/screen-reader claim exists until these steps actually occur. + +## Source seals + +| File | SHA256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `32be43dd2ce112e30017b6b9bf706607f942ca574b2415f74237c296f79614c5` | +| `src/bin/desktop/scene_ui.rs` | `68d3eed0bdfd1ee424854325349d3e27391cbdf85b352f4c7acb5a0c7d5a11ec` | +| `src/bin/desktop/settings.rs` | `72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4` | +| `src/bin/desktop/viewport.rs` | `a2ae713e3a06f2370e72300593d208c1991aa201b41e0924cd87268a48a296e1` | +| `src/bin/desktop/timeline_ui_tests.rs` | `653848ce12986065b966c6ab2d97b23c2e7da4cdf5ec7f5a6b99c1e5a9ef2c35` | diff --git a/docs/plans/ui-refinement/reports/scene-authoring-admission.md b/docs/plans/ui-refinement/reports/scene-authoring-admission.md index 61a2abd2..ce8a460d 100644 --- a/docs/plans/ui-refinement/reports/scene-authoring-admission.md +++ b/docs/plans/ui-refinement/reports/scene-authoring-admission.md @@ -14,7 +14,10 @@ no squash of original M2a or imported backend evidence. No numerical/model tests preserve history if fresh ID admission fails; include initial CLI admission) and successful run-context changes; select draft versus immutable retained geometry consistently; refuse new draft launches containing any force object before path/recording integration. -- Exact UI-only tests in scene_ui.rs or desktop/scene_ui_tests.rs. No viewport, +- viewport.rs mode-control guard only: pending authored drafts stay in Appearance + and cannot enter independent unsupported numerical labs. No picking/painting + or lab dispatch changes. +- Exact UI-only tests in scene_ui.rs or desktop/scene_ui_tests.rs. No other viewport, solver/core recording/run_sets changes in this first authoring slice. ## Behavior and identities diff --git a/docs/plans/ui-refinement/reports/scene-authoring-source-review.md b/docs/plans/ui-refinement/reports/scene-authoring-source-review.md new file mode 100644 index 00000000..ae50109d --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-authoring-source-review.md @@ -0,0 +1,235 @@ +# Independent scene authoring source review + +2026-10-20. Scope: uncommitted authoring increment on +`implementation/scene-authoring-ui-20261020` in `/workspace/Rheon-ui-authoring`, +parent `7dcbfa67578875dd266020be589e2fbea40467f8`, following canonical `43ec505` +and the eight separately preserved UI commits. This is source review, not native +interaction, scientific validation or a certificate for the earlier draft PR. +Reviewer executes no tests, numerical/model workloads, imports, recording/SQLite +operations, filesystem fixtures or cleanup. Only this report is authored. + +## Source and test boundary + +Reviewed the complete new `scene_ui.rs`, the main App composition/context/launch +diff, the quantity-label association and the subsequently admitted viewport +mode guard against `scene-authoring-admission.md`. Core/recording/runtime and +versioned run-set files are outside this slice. The viewport addition only +projects a pending-authored-draft flag, chooses Appearance and disables other +mode controls before their independent lab dispatch; it adds no force evaluator, +pick, outline or manipulator. + +The four initially admitted exact tests are all `scene_ui::tests`: + +- `scene_identity_context_removal_and_exhaustion_preserve_canonical_values` +- `scene_pointer_add_select_edit_collapse_and_retained_context` +- `scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation` +- `scene_force_refusal_projects_disabled_run_without_dispatch` + +Their entire frame/click/key/replace chain uses a local egui Context, literal +authoring values and direct SceneUi/run-controls painting. `ForceScene::freeze` +only validates and deep-copies immutable input; it never calls acceleration or +a solver. Euler/quaternion conversion here is ordinary UI coordinate arithmetic, +not a numerical simulation fixture. The App test calls `run_controls` but never +dispatches its return, calls App::update/start/Worker, or creates history. App +default retains absent worker/history/runtime objects; its added SceneUi default +constructs an empty canonical document and scalar IDs. RecordingLimits defaults +are constants, Inspector defaults retain no reader and ExportFile::new stores +path strings only. App Drop's join branch requires a worker, which the test +asserts absent. No model import, recording, file output, process or cleanup path +is reached. This source boundary admits only those individually enumerated +fully qualified exact probes, not a substring suite or uninspected new helper. + +## Ownership, units and transactions + +App owns one SceneUi containing the sole canonical ForceScene and selection. +Selected properties directly edit that document; transient Euler controls only +write a new quaternion after an explicit edit/reset. Untouched orientations are +not normalized or rewritten. Metres, seconds, m/s², N/m³ and degrees are labelled; +the supplied domain retains quaternion/SI storage, support boundary conventions, +activation and local-density semantics. Changing unit kind explicitly reinterprets +the authored number, without inventing a density conversion. Gravity remains +separate/additive. Properties validate through the backend freeze API, including +disabled objects; invalid draft values remain visible and cannot execute. + +The checked monotonic allocator shares nonzero IDs across the one model slot and +forces. Remove does not recycle identity; exhaustion refuses instead of wrapping. +Model replacement requests a fresh ID before committing scene identity. The main +bridge restores the previous accepted mesh and avoids reset on refusal; removal +and successful transactional Apply retain the existing explicit history-clearing +behavior. CLI model admission is included. There is no model collection, import +schema or save/load interface added by this slice, so future import must still +enforce whole-scene collision and exhaustion checks. + +Draft and Retained are explicit contexts independent of worker presence. The +intended retained projection uses immutable Request geometry and absent/unknown +legacy authored-force metadata, never reconstructing it from the current draft. +Editing and adding forces are disabled during an active worker or retained view. +Collapse retains selection; removal/replacement and context changes clear it. +The right list is a future common selection owner, not implemented viewport +picking. Accepted model properties remain read-only; existing setup Apply owns +collision admission. Runtime force propagation, persistence, outlines and handles +are not claimed. + +Both new preview and configured draft launches refuse any nonempty authored +document, including disabled objects, before output creation/Worker entry. Legacy +frozen reruns intentionally retain their own recorded intent and do not consume +the current draft. The independent viewport lab guard is consistent with that +unsupported-workflow policy. Narrow composition reuses the existing run/status/ +Cancel owner in a top strip while the scene drawer replaces setup; it does not +create another worker or action owner. + +The settings change captures the existing visible label and associates its ID +with the returned quantity response. It does not alter the precise formatter, +numeric parser, conversions, ranges, widget order, auto-ID allocation or canonical +values. It is an intentional delta from the prior precision source hash, not a +claim that the whole file remains byte-identical. + +## Findings tracked during review + +1. **Repaired:** Draft particle view initially received the retained Inspector. + Main now filters that projection by retained context, preventing historical + points from being interpreted with draft dimensions. Particle-view controls + clear their display cache on absent Inspector; retained data is not deleted. +2. **Repaired:** Retained-to-Retained new runs could preserve stale model + selection. `begin_run` now clears selection every time; a pure assertion was + added without constructing a worker or run. +3. **Repaired:** An invalid future draft error was visible while inspecting a + retained legacy run. Validation labels are now scoped to Draft. Name help and + validation wording identify the backend's 256 UTF-8-byte limit; the TextEdit's + character limit remains only an editor aid and does not replace validation. +4. **Open at this review checkpoint:** Draft paints the real timeline with zero + count, so pinned egui Slider's default Always clamping writes step zero even + while disabled. This loses the retained index. Selecting Retained after a + precomputed Draft count can cause the same issue for one frame. Preserve the + real retained playback state by omitting/discarding Draft timeline interaction + and selecting the actual count/frame after scene context input settles. +5. **Open at this review checkpoint:** The independent “Compare Rheon / trained + AI” entry checks only worker presence and can enter a different runtime owner + without consuming/refusing an authored draft. Apply the same unsupported-draft + refusal before entering it, or explicitly admit a different scoped policy. + “Compare both pressure solvers” already routes through guarded `start(true)`. + +No current test execution, visual geometry or native evidence is claimed in this +initial source-admission receipt. Native widths/scales, right-panel focus/order, +keyboard add/select/remove, full transform/support editing, active-worker input +locking, top run-strip geometry and model-import rollback interaction still need +their corresponding focused/native evidence. The complete App update must not be +used as an allegedly inert test harness: it can load Inspector fields or initialize +independent numerical labs. Pending source fixes and final hashes/results will be +recorded below before final source disposition. + +## Follow-up source corrections and fifth exact probe + +Both previously open findings above are repaired in the re-inspected source. +`App::scene_timeline_ui` returns None for Draft without painting/committing real +playback controls. The App selects retained count/frame only after scene context +input has settled, so a Draft-origin zero count cannot clamp the retained index +on a Retained transition. The AI comparison entry now requires the same draft +launch readiness; frozen archive rerun policy remains deliberately separate. + +The fifth inspected exact test, +`scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history`, +uses App default, a literal PreviewFrame and local Context. It passes nonzero +step 7, track 1 and exact play-time bits through Draft/Retained/Draft/Retained +via `scene_timeline_ui` only. Retained count is a literal 10; it never calls +selected_frame, creates history, loads fields, dispatches actions or calls +App::update. The existing refusal test was strengthened to physically click +Simulate, but its projection captures only whether run_controls returned an +intent. Even a regressed enabled button cannot launch a worker in that helper. +These complete helper paths remain inert; no reviewer test execution occurred. + +The admitted viewport guard's default is one false scalar. Main sets it only for +Draft plus nonempty canonical authoring objects. At viewport entry it chooses +Appearance before dispatch and disables Particles/Obstacle/Transport choices; +it does not initialize those independent workflows or claim force visualization. +The five scene tests never call Viewport::show. Guard behavior is therefore +source-inspected, with actual viewport/navigation coverage still pending. + +No remaining blocking source finding has been identified after these repairs. +The focused probe admission is five fully qualified, list-enumerated exact names +only. Final source hashes, implementation-owner checks and actual evidence +limits must accompany the resulting checkpoint; this disposition is not native +or complete scene-feature acceptance. + +## Final source disposition and checked evidence + +No blocking source finding remains for this bounded authoring increment after +the corrections above. Source review supports the next implementation checkpoint +with the stated feature/evidence limits. It does not accept authored-force +execution, persistence, viewport picking/outlines/manipulators or native UI. + +Read the implementation owner's final logs, without rerunning any test: + +| Log under `/tmp/rheon-ui-scene-authoring-` | Checked result | +| --- | --- | +| `final-ids.log` | One exact identity/removal/exhaustion test passed | +| `exact-pointer-2.log` | One exact pointer add/select/edit/units/collapse/context test passed at 900/1250px widths | +| `final-validation.log` | One exact invalid-draft/frozen-snapshot/rotation-values test passed | +| `final-refusal.log` | One exact disabled-Simulate intent-only projection test passed | +| `final-context.log` | One exact history-free Draft/Retained index roundtrip test passed | +| `final-precision.log` | One exact existing material focus/Tab bit-preservation test passed after label association | +| `clippy-1.log` | Desktop/test strict Clippy completed successfully in 52.99 seconds | + +Each runtime log reports one selected pass and 176 filtered out; list-only +enumeration reports 177 tests. The retained `exact-pointer-1.log` failed at a +literal `0 N/m³` caption lookup. Its test-only repair locates the actually shaped +caption by exact unit suffix and numeric prefix equal to zero, then clicks through +the same clipped-pointer helper. This preserves the canonical edit/unit assertion +and adds no workload. It is not evidence that an unexecuted native model or +scientific state was inspected. Current locked-check/format outcomes and any +additional individually selected precision gates remain the implementation +owner's final verification receipt; this reviewer independently ran only +`git diff --check`, which passed. + +| Final reviewed source | SHA256 | +| --- | --- | +| `src/bin/desktop/scene_ui.rs` (including five tests) | `68d3eed0bdfd1ee424854325349d3e27391cbdf85b352f4c7acb5a0c7d5a11ec` | +| `src/bin/rheon-desktop.rs` | `32be43dd2ce112e30017b6b9bf706607f942ca574b2415f74237c296f79614c5` | +| `src/bin/desktop/settings.rs` | `72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4` | +| `src/bin/desktop/viewport.rs` | `a2ae713e3a06f2370e72300593d208c1991aa201b41e0924cd87268a48a296e1` | + +The tests establish the inspected pure-state and egui input paths, not full App +update/worker behavior. Full mesh-import failure rollback, actual active-worker +locking and viewport mode refusal are source-inspected here. The pointer test +reserves at least 300px viewport width at 820px height; it does not assert every +expanded property/control's bounds at 900×650/150% or screen-reader usability. +Native validation must cover those states and the persistent top run strip. +Retained archive integrity, recording completeness and force provenance remain +their existing distinct contracts; the draft scene is not evidence of any of +them and is not persisted by this increment. + +## Final retained evidence receipt + +Independently read and verified +`../evidence/scene-authoring-20261020/manifest.json`, SHA256 +`5b586883c0ecb31124ae8dc4154a4d2302aaa69525bf9a27652b512fb04b6ec6`. +All five source seals, including unchanged timeline test source +`653848ce12986065b966c6ab2d97b23c2e7da4cdf5ec7f5a6b99c1e5a9ef2c35`, +match the working files. All 20 retained logs match their declared byte counts, +SHA256 values and original `/tmp` bytes. Git attributes mark the retained logs +binary with text conversion disabled. This verifies the checked evidence packet's +identity; it is not a signed binary provenance certificate. + +The manifest's 14 distinct exact names each occur once in the 177-test list-only +enumeration and each match one retained passing log with one selected pass, +zero failures/ignored and 176 filtered out. Those are five scene tests, four +timeline tests and five material/precision tests, all on the previously inspected +inert helper paths. The raw earlier pointer-caption failure is retained alongside +its corrected pass. No failed log was overwritten or promoted to a pass. + +The final retained gate logs show completed Clippy in 52.99 seconds and locked +check in 1.37 seconds. Format and whitespace logs are empty, consistent with the +implementation owner's reported successful exits; empty output by itself does +not independently encode a process exit status. Reviewer reran only read/hash +checks and `git diff --check`, which was clean. No tests/builds/workloads were +executed for this evidence review, and no source seal changed. + +The native handoff's five seals match this packet and it explicitly requires +idle-only native inspection; existing separately approved retained/model fixtures +are prerequisites for those interactions, not permission to generate numerical +evidence. Native screenshots and screen-reader behavior remain absent. Actual +model-import rollback, active-worker/progress/Cancel, viewport mode-guard execution, +picking/outlines/handles, and force runtime/archive acceptance remain unverified. +The completed focused gates support this bounded source checkpoint with those +gaps; they do not change the final source disposition or authorize additional +workloads. The review itself performed no commit, push or PR operation. diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs new file mode 100644 index 00000000..da689c15 --- /dev/null +++ b/src/bin/desktop/scene_ui.rs @@ -0,0 +1,713 @@ +//! Canonical authoring values and one scene selection. No numerical execution. +use eframe::egui; +use rheon::{ + ForceKind, ForceObject, ForceScene, ForceSupport, ForceTime, ForceTransform, ForceUnits, + SceneObjectId, +}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum SceneContext { + #[default] + Draft, + Retained, +} +#[derive(Clone, Copy)] +pub struct ModelSummary { + pub triangles: usize, + pub bounds: ([f64; 3], [f64; 3]), +} +#[derive(Clone, Copy, Debug)] +pub enum NewForce { + Directional, + Radial, + Vortex, +} +impl NewForce { + fn label(self) -> &'static str { + match self { + Self::Directional => "Directional", + Self::Radial => "Radial", + Self::Vortex => "Vortex", + } + } +} +pub struct SceneUi { + forces: ForceScene, + selected: Option, + next_id: Option, + model_id: Option, + retained_model_id: Option, + pub context: SceneContext, + pub open: bool, + error: Option, +} +impl Default for SceneUi { + fn default() -> Self { + Self { + forces: ForceScene::default(), + selected: None, + next_id: Some(1), + model_id: None, + retained_model_id: None, + context: SceneContext::Draft, + open: false, + error: None, + } + } +} +impl SceneUi { + pub fn draft(&self) -> &ForceScene { + &self.forces + } + pub fn selected(&self) -> Option { + self.selected + } + pub fn model_id(&self) -> Option { + match self.context { + SceneContext::Draft => self.model_id, + SceneContext::Retained => self.retained_model_id, + } + } + fn allocate(&mut self) -> Result { + let id = self + .next_id + .filter(|id| *id != 0) + .ok_or("Scene object IDs exhausted; previous scene retained")?; + self.next_id = id.checked_add(1); + Ok(SceneObjectId(id)) + } + /// Caller restores old accepted geometry if admission fails. No state changes on error. + pub fn replace_model(&mut self, present: bool) -> Result<(), String> { + let replacement = if present { + Some(self.allocate()?) + } else { + None + }; + if self.selected == self.model_id { + self.selected = None; + } + self.model_id = replacement; + Ok(()) + } + pub fn begin_run(&mut self, model_present: bool) { + self.selected = None; + self.retained_model_id = if model_present { self.model_id } else { None }; + self.set_context(SceneContext::Retained); + } + pub fn clear_run(&mut self) { + self.retained_model_id = None; + self.set_context(SceneContext::Draft); + } + fn set_context(&mut self, context: SceneContext) { + if context != self.context { + self.selected = None; + self.context = context; + } + } + /// This is the sole selection owner. Viewport picking will call this same API. + pub fn select(&mut self, id: Option) { + self.selected = id.filter(|id| { + Some(*id) == self.model_id() + || (self.context == SceneContext::Draft + && self.forces.objects.iter().any(|o| o.id == *id)) + }); + } + pub fn launch_readiness(&self) -> Result<(), String> { + if self.draft().objects.is_empty() { + Ok(()) + } else { + Err("Authored forces cannot run yet: runtime and archive integration is pending. Remove authored forces to run the existing setup.".into()) + } + } + fn validate(&mut self) { + self.error = self.forces.freeze().err().map(|e| { + format!( + "Invalid authored force: {e}. Check name (256 UTF-8 bytes), support size, orientation and times." + ) + }); + } + fn add(&mut self, kind: NewForce) -> Result { + if self.forces.objects.len() >= rheon::MAX_FORCE_OBJECTS { + return Err("Scene limit: 256 authored forces".into()); + } + let id = self.allocate()?; + self.forces.objects.push(ForceObject { + id, + name: format!("{} {}", kind.label(), id.0), + enabled: true, + transform: ForceTransform::default(), + units: ForceUnits::Acceleration, + kind: match kind { + NewForce::Directional => ForceKind::Directional { vector: [0.0; 3] }, + NewForce::Radial => ForceKind::Radial { strength: 0.0 }, + NewForce::Vortex => ForceKind::Vortex { strength: 0.0 }, + }, + support: ForceSupport::Global, + time: ForceTime::default(), + }); + self.selected = Some(id); + self.validate(); + Ok(id) + } + fn remove_selected(&mut self) { + if let Some(id) = self.selected { + self.forces.objects.retain(|o| o.id != id); + } + self.selected = None; + self.validate(); + } + pub fn show( + &mut self, + ctx: &egui::Context, + has_run: bool, + active_worker: bool, + model: Option, + ) { + if !self.open { + egui::SidePanel::right("scene-collapsed") + .exact_width(64.0) + .resizable(false) + .show(ctx, |ui| { + if ui.button("Scene").clicked() { + self.open = true; + } + }); + return; + } + egui::SidePanel::right("scene-properties").default_width(272.0).min_width(240.0).max_width(340.0).show(ctx,|ui| { + ui.horizontal_wrapped(|ui| {ui.heading("Scene");if ui.button("Hide scene").clicked(){self.open=false;}}); + let previous=self.context; + ui.horizontal_wrapped(|ui| { + ui.selectable_value(&mut self.context,SceneContext::Draft,"Draft"); + ui.add_enabled_ui(has_run,|ui|{ui.selectable_value(&mut self.context,SceneContext::Retained,"Retained run");}); + }); + if previous!=self.context {self.selected=None;} + let editable=self.context==SceneContext::Draft && !active_worker; + if self.context==SceneContext::Draft && !self.forces.objects.is_empty() { + ui.colored_label(egui::Color32::YELLOW,"Authored-force runs unavailable") + .on_hover_text("Runtime and archive integration is pending. No authored force will be silently ignored."); + } + if self.context==SceneContext::Draft && let Some(error)=&self.error {ui.colored_label(egui::Color32::LIGHT_RED,error);} + egui::ScrollArea::vertical().id_salt("scene-properties-scroll").show(ui,|ui| { + if let Some(id)=self.model_id() { + let response=ui.selectable_label(self.selected()==Some(id),"Collision model"); + if response.clicked(){self.select(Some(id));} + } + ui.heading("Authored forces"); + if self.context==SceneContext::Draft { + ui.add_enabled_ui(editable,|ui| {ui.menu_button("Add force",|ui| { + for kind in [NewForce::Directional,NewForce::Radial,NewForce::Vortex] { + if ui.button(kind.label()).clicked() {if let Err(e)=self.add(kind){self.error=Some(e);}ui.close();} + } + });}); + egui::ScrollArea::vertical().id_salt("force-object-list").max_height(160.0).show(ui,|ui| { + for object in &self.forces.objects { + let label=if object.enabled {object.name.clone()} else {format!("{} (disabled)",object.name)}; + if ui.push_id(object.id.0, |ui| ui.selectable_label(self.selected==Some(object.id),label)).inner.clicked() {self.selected=Some(object.id);} + } + }); + ui.small("Gravity remains additive.").on_hover_text("Acceleration fields add to configured gravity. Avoid applying Earth gravity twice. Force density divides once by local mass density, never particle mass."); + } else {ui.label("Authored-force metadata unavailable").on_hover_text("This legacy retained run has no canonical authored-force document. Its physics is not inferred from the current draft.");} + ui.separator(); + ui.heading("Properties"); + if self.selected.is_some() && self.selected==self.model_id() { + if let Some(model)=model {ui.label(format!("{} triangles",model.triangles));ui.label(format!("Bounds {:?} … {:?} m",model.bounds.0,model.bounds.1));ui.small("Accepted geometry · placement uses Import / apply mesh in setup.");} + } else if self.context==SceneContext::Draft { + if let Some(object)=self.forces.objects.iter_mut().find(|o|Some(o.id)==self.selected) { + let changed=ui.push_id(("force-properties",object.id.0), |ui| ui.add_enabled_ui(editable,|ui|force_properties(ui,object)).inner).inner; + if changed {self.validate();} + if ui.add_enabled(editable,egui::Button::new("Remove force")).clicked(){self.remove_selected();} + } else {ui.label("Select an object");} + } else {ui.label("Select an object");} + }); + }); + } +} +fn quantity(ui: &mut egui::Ui, label: &str, value: &mut f64, unit: &str) -> bool { + super::settings::quantity(ui, label, value, unit, 0.01, -f64::MAX..=f64::MAX).changed() +} +fn force_properties(ui: &mut egui::Ui, object: &mut ForceObject) -> bool { + let mut changed = false; + let name_label = ui + .label("Name") + .on_hover_text("At most 256 UTF-8 bytes; disabled objects are validated too."); + changed |= ui + .add( + egui::TextEdit::singleline(&mut object.name) + .desired_width(ui.available_width()) + .char_limit(256), + ) + .labelled_by(name_label.id) + .changed(); + changed |= ui.checkbox(&mut object.enabled, "Enabled").changed(); + ui.horizontal_wrapped(|ui| { + let units_label = ui.label("Units"); + egui::ComboBox::from_id_salt("force-units") + .selected_text(match object.units { + ForceUnits::Acceleration => "m/s²", + ForceUnits::ForceDensity => "N/m³", + }) + .show_ui(ui, |ui| { + changed |= ui + .selectable_value( + &mut object.units, + ForceUnits::Acceleration, + "Acceleration · m/s²", + ) + .changed(); + changed |= ui + .selectable_value( + &mut object.units, + ForceUnits::ForceDensity, + "Force density · N/m³", + ) + .changed(); + }).response.labelled_by(units_label.id).on_hover_text("Changing units reinterprets the authored numbers. No automatic density conversion is applied here."); + }); + let unit = match object.units { + ForceUnits::Acceleration => "m/s²", + ForceUnits::ForceDensity => "N/m³", + }; + match &mut object.kind { + ForceKind::Directional { vector } => { + for (d, label) in ["Vector X", "Vector Y", "Vector Z"].iter().enumerate() { + changed |= quantity(ui, label, &mut vector[d], unit); + } + } + ForceKind::Radial { strength } => { + changed |= quantity(ui, "Strength", strength, unit); + ui.small("Positive repels · negative attracts").on_hover_text("Constant magnitude before falloff; zero at the centre. Not an inverse-square law."); + } + ForceKind::Vortex { strength } => { + changed |= quantity(ui, "Strength", strength, unit); + ui.small("Around local +Y").on_hover_text("Positive strength follows the right-hand rule: at local +X the field points toward −Z. Zero on the axis."); + } + } + ui.collapsing("Transform",|ui| { + for(d,label)in["Position X","Position Y","Position Z"].iter().enumerate(){changed|=quantity(ui,label,&mut object.transform.translation[d],"m");} + let mut angles=euler_degrees(object.transform.rotation);let mut rotate=false; + for(d,label)in["Rotation X","Rotation Y","Rotation Z"].iter().enumerate(){rotate|=super::settings::quantity(ui,label,&mut angles[d],"°",1.0,-360.0..=360.0).changed();} + if rotate {object.transform.rotation=quaternion(angles);changed=true;} + if ui.button("Reset orientation").clicked() { object.transform.rotation=ForceTransform::default().rotation; changed=true; } + ui.small("Local XYZ orientation").on_hover_text("Local-to-world rotation Rz·Ry·Rx, in degrees. Translation and support dimensions are metres; no scale or parenting."); + }); + ui.collapsing("Support & activation",|ui| { + let mut support=match object.support {ForceSupport::Global=>0,ForceSupport::Sphere{..}=>1,ForceSupport::Box{..}=>2}; + let before=support; + egui::ComboBox::from_id_salt("force-support").selected_text(["Global","Sphere","Box"][support]).show_ui(ui,|ui|{for(i,label)in["Global","Sphere","Box"].iter().enumerate(){ui.selectable_value(&mut support,i,*label);}}); + if before!=support {object.support=match support {1=>ForceSupport::Sphere{radius:0.25,edge_fraction:0.25},2=>ForceSupport::Box{half_extent:[0.25;3],edge_fraction:0.25},_=>ForceSupport::Global};changed=true;} + match &mut object.support { + ForceSupport::Global=>{}, + ForceSupport::Sphere{radius,edge_fraction}=>{changed|=quantity(ui,"Radius",radius,"m");changed|=quantity(ui,"Edge fraction",edge_fraction,"");}, + ForceSupport::Box{half_extent,edge_fraction}=>{for(d,label)in["Half-size X","Half-size Y","Half-size Z"].iter().enumerate(){changed|=quantity(ui,label,&mut half_extent[d],"m");}changed|=quantity(ui,"Edge fraction",edge_fraction,"");} + } + changed|=quantity(ui,"Start",&mut object.time.start_seconds,"s"); + let mut finite_end=object.time.end_seconds.is_some(); + if ui.checkbox(&mut finite_end,"End time").changed(){object.time.end_seconds=finite_end.then_some(object.time.start_seconds+1.0);changed=true;} + if let Some(end)=&mut object.time.end_seconds {changed|=quantity(ui,"End (exclusive)",end,"s");} + ui.small("Sampled at accepted interval start").on_hover_text("Activation is [start,end). Steps are not split at endpoints. Sphere outer surface is excluded; box bounds are half-open. Falloff edge fraction is0–1; box falloff uses max-norm shells."); + }); + changed +} +fn quaternion(degrees: [f64; 3]) -> [f64; 4] { + let [x, y, z] = degrees.map(|v| v.to_radians() * 0.5); + let (sx, cx) = x.sin_cos(); + let (sy, cy) = y.sin_cos(); + let (sz, cz) = z.sin_cos(); + [ + sx * cy * cz - cx * sy * sz, + cx * sy * cz + sx * cy * sz, + cx * cy * sz - sx * sy * cz, + cx * cy * cz + sx * sy * sz, + ] +} +fn euler_degrees([x, y, z, w]: [f64; 4]) -> [f64; 3] { + [ + (2.0 * (w * x + y * z)).atan2(1.0 - 2.0 * (x * x + y * y)), + (2.0 * (w * y - z * x)).clamp(-1.0, 1.0).asin(), + (2.0 * (w * z + x * y)).atan2(1.0 - 2.0 * (y * y + z * z)), + ] + .map(f64::to_degrees) +} + +#[cfg(test)] +mod tests { + use super::*; + fn context() -> egui::Context { + let ctx = egui::Context::default(); + ctx.enable_accesskit(); + ctx.style_mut(|s| s.animation_time = 0.0); + ctx + } + fn frame( + ctx: &egui::Context, + scene: &mut SceneUi, + width: f32, + events: Vec, + ) -> (egui::FullOutput, egui::Rect) { + let mut available = egui::Rect::NOTHING; + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, 820.0), + )), + events, + ..Default::default() + }, + |ctx| { + if width >= 1100.0 { + egui::SidePanel::left("inert-setup") + .exact_width(280.0) + .show(ctx, |_| {}); + } + scene.show(ctx, true, false, None); + available = ctx.available_rect(); + }, + ); + assert!(available.width() >= 300.0, "viewport width {available:?}"); + (output, available) + } + fn text_rect(output: &egui::FullOutput, label: &str, last: bool) -> egui::Rect { + let matches: Vec<_> = output + .shapes + .iter() + .filter_map(|s| match &s.shape { + egui::Shape::Text(t) if t.galley.text() == label => { + Some(egui::Rect::from_min_size(t.pos, t.galley.size()).intersect(s.clip_rect)) + } + _ => None, + }) + .filter(|r| r.is_positive()) + .collect(); + *if last { + matches.last() + } else { + matches.first() + } + .unwrap_or_else(|| panic!("missing visible {label}")) + } + fn click( + ctx: &egui::Context, + scene: &mut SceneUi, + width: f32, + output: &egui::FullOutput, + label: &str, + last: bool, + ) { + let pos = text_rect(output, label, last).center(); + for pressed in [true, false] { + let _ = frame( + ctx, + scene, + width, + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + ); + } + } + fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + [true, false] + .map(|pressed| egui::Event::Key { + key, + physical_key: None, + pressed, + repeat: false, + modifiers, + }) + .into() + } + fn replace(ctx: &egui::Context, scene: &mut SceneUi, width: f32, text: &str) { + let _ = frame( + ctx, + scene, + width, + key( + egui::Key::A, + egui::Modifiers { + ctrl: true, + command: true, + ..Default::default() + }, + ), + ); + let _ = frame(ctx, scene, width, vec![egui::Event::Text(text.into())]); + } + #[test] + fn scene_identity_context_removal_and_exhaustion_preserve_canonical_values() { + let mut scene = SceneUi::default(); + scene.replace_model(true).unwrap(); + let model = scene.model_id().unwrap(); + let a = scene.add(NewForce::Directional).unwrap(); + let b = scene.add(NewForce::Radial).unwrap(); + let c = scene.add(NewForce::Vortex).unwrap(); + assert_eq!([model.0, a.0, b.0, c.0], [1, 2, 3, 4]); + assert_eq!(scene.selected(), Some(c)); + let frozen = scene.draft().freeze().unwrap(); + scene.forces.objects[0].name = "Changed draft".into(); + assert_ne!( + scene.draft().objects[0].name, + frozen.scene().objects[0].name + ); + scene.select(Some(a)); + scene.remove_selected(); + assert_eq!(scene.selected(), None); + assert!(scene.draft().objects.iter().all(|o| o.id != a)); + scene.begin_run(true); + assert_eq!(scene.context, SceneContext::Retained); + scene.select(Some(b)); + assert_eq!(scene.selected(), None); + scene.select(Some(model)); + assert_eq!(scene.selected(), Some(model)); + scene.begin_run(true); + assert_eq!(scene.selected(), None); + scene.set_context(SceneContext::Draft); + assert_eq!(scene.selected(), None); + scene.next_id = Some(u64::MAX); + let last = scene.add(NewForce::Directional).unwrap(); + assert_eq!(last.0, u64::MAX); + let before = scene.forces.clone(); + let selected = scene.selected(); + assert!(scene.add(NewForce::Radial).is_err()); + assert_eq!(scene.forces, before); + assert!(scene.replace_model(true).is_err()); + assert_eq!(scene.model_id(), Some(model)); + assert_eq!(scene.selected(), selected); + scene.replace_model(false).unwrap(); + assert_eq!(scene.model_id(), None); + } + #[test] + fn scene_pointer_add_select_edit_collapse_and_retained_context() { + for width in [900.0, 1250.0] { + let ctx = context(); + let mut scene = SceneUi { + open: true, + ..Default::default() + }; + for kind in ["Directional", "Radial", "Vortex"] { + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, "Add force", false); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, kind, false); + assert_eq!(scene.selected(), scene.forces.objects.last().map(|o| o.id)); + } + assert_eq!(scene.forces.objects.len(), 3); + let selected = scene.selected(); + let name = scene.forces.objects[2].name.clone(); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, &name, true); + replace(&ctx, &mut scene, width, "Swirl"); + assert_eq!(scene.forces.objects[2].name, "Swirl"); + assert_eq!(scene.selected(), selected); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, "m/s²", false); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click( + &ctx, + &mut scene, + width, + &output, + "Force density · N/m³", + false, + ); + assert_eq!(scene.forces.objects[2].units, ForceUnits::ForceDensity); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + let caption = output + .shapes + .iter() + .find_map(|shape| match &shape.shape { + egui::Shape::Text(text) + if text + .galley + .text() + .strip_suffix(" N/m³") + .and_then(|v| v.parse::().ok()) + == Some(0.0) => + { + Some(text.galley.text().to_owned()) + } + _ => None, + }) + .expect("visible zero strength with force-density units"); + click(&ctx, &mut scene, width, &output, &caption, false); + replace(&ctx, &mut scene, width, "-2.5"); + assert_eq!( + scene.forces.objects[2].kind, + ForceKind::Vortex { strength: -2.5 } + ); + assert!(scene.forces.freeze().is_ok()); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, "Enabled", false); + assert!(!scene.forces.objects[2].enabled); + assert!(scene.launch_readiness().is_err()); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, "Hide scene", false); + assert!(!scene.open); + assert_eq!(scene.selected(), selected); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click(&ctx, &mut scene, width, &output, "Scene", false); + assert!(scene.open); + assert_eq!(scene.selected(), selected); + let before = scene.forces.clone(); + scene.begin_run(false); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + assert!(text_rect(&output, "Authored-force metadata unavailable", false).is_positive()); + assert_eq!(scene.selected(), None); + assert_eq!(scene.forces, before); + click(&ctx, &mut scene, width, &output, "Draft", false); + assert_eq!(scene.context, SceneContext::Draft); + assert_eq!(scene.forces, before); + } + } + #[test] + fn scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation() { + let ctx = context(); + let mut scene = SceneUi { + open: true, + ..Default::default() + }; + scene.add(NewForce::Radial).unwrap(); + let snapshot = scene.draft().freeze().unwrap(); + scene.forces.objects[0].support = ForceSupport::Sphere { + radius: 0.0, + edge_fraction: 0.25, + }; + scene.validate(); + assert!(scene.error.is_some()); + assert!(scene.draft().freeze().is_err()); + assert!(scene.launch_readiness().is_err()); + assert_eq!(snapshot.scene().objects[0].support, ForceSupport::Global); + let (output, _) = frame(&ctx, &mut scene, 900.0, vec![]); + assert!(output.shapes.iter().any(|s|matches!(&s.shape,egui::Shape::Text(t) if t.galley.text().contains("Invalid authored force")))); + scene.forces.objects[0].support = ForceSupport::Sphere { + radius: 0.4, + edge_fraction: 0.25, + }; + scene.validate(); + assert!(scene.error.is_none()); + for angles in [[0.0, 0.0, 0.0], [30.0, 45.0, -90.0], [180.0, -90.0, 360.0]] { + scene.forces.objects[0].transform.rotation = quaternion(angles); + assert!(scene.draft().freeze().is_ok()); + } + } + #[test] + fn scene_force_refusal_projects_disabled_run_without_dispatch() { + // Paint/capture intent only: even a broken guard cannot start work here. + fn project( + ctx: &egui::Context, + app: &mut super::super::App, + events: Vec, + ) -> (egui::FullOutput, bool) { + let mut requested = false; + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(900.0, 650.0), + )), + events, + ..Default::default() + }, + |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + requested = app.run_controls(ui).is_some(); + }); + }, + ); + (output, requested) + } + let ctx = context(); + let mut app = super::super::App::default(); + app.scene.add(NewForce::Directional).unwrap(); + let (output, requested) = project(&ctx, &mut app, vec![]); + assert!(!requested); + assert!(output.shapes.iter().any(|s|matches!(&s.shape,egui::Shape::Text(t) if t.galley.text()=="Authored-force runs unavailable"))); + let pos = text_rect(&output, "Simulate", false).center(); + for pressed in [true, false] { + let (_, requested) = project( + &ctx, + &mut app, + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + ); + assert!(!requested); + } + assert!(app.worker.is_none() && app.history.is_none() && app.running_request.is_none()); + app.scene.forces.objects[0].enabled = false; + assert!(app.scene.launch_readiness().is_err()); + } + #[test] + fn scene_draft_timeline_roundtrip_preserves_retained_index_without_history() { + let ctx = context(); + let mut app = super::super::App::default(); + app.timeline_step = 7; + app.track = 1; + app.follow_live = false; + app.play_time = 1.25; + let before = (app.timeline_step, app.track, app.play_time.to_bits()); + let selected = super::super::PreviewFrame { + method: rheon::PressureImplementation::JacobiPcgV1, + step: 7, + time: 1.25, + seconds: 0.002, + edge: 1, + nonzero_tracer_cells: 0, + density: vec![], + report: None, + particle_report: None, + material_color: None, + }; + for context in [ + SceneContext::Draft, + SceneContext::Retained, + SceneContext::Draft, + SceneContext::Retained, + ] { + app.scene.set_context(context); + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(900.0, 650.0), + )), + ..Default::default() + }, + |ctx| { + let painted = app.scene_timeline_ui( + ctx, + if context == SceneContext::Retained { + 10 + } else { + 0 + }, + if context == SceneContext::Retained { + Some(&selected) + } else { + None + }, + ); + assert_eq!(painted.is_some(), context == SceneContext::Retained); + }, + ); + assert_eq!( + (app.timeline_step, app.track, app.play_time.to_bits()), + before + ); + } + assert!(app.worker.is_none() && app.history.is_none() && app.running_request.is_none()); + } +} diff --git a/src/bin/desktop/settings.rs b/src/bin/desktop/settings.rs index e7eecfbc..a0fa6361 100644 --- a/src/bin/desktop/settings.rs +++ b/src/bin/desktop/settings.rs @@ -360,7 +360,7 @@ pub fn quantity( range: std::ops::RangeInclusive, ) -> egui::Response { ui.horizontal_wrapped(|ui| { - ui.label(label); + let label = ui.label(label); precise_drag( ui, *value, @@ -370,6 +370,7 @@ pub fn quantity( .max_decimals(8) .suffix(format!(" {unit}")), ) + .labelled_by(label.id) }) .inner } diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index 07a162ee..386894cb 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -53,6 +53,8 @@ enum ViewMode { Transport, } pub struct Viewport { + /// Draft authored fields have no runtime/preview integration yet. + pub authored_forces_pending: bool, pub collision_mesh: Option>, pub camera: OrbitCamera, engineering: Option, @@ -72,6 +74,7 @@ pub struct Viewport { impl Default for Viewport { fn default() -> Self { Self { + authored_forces_pending: false, camera: OrbitCamera::default(), engineering: None, mode: ViewMode::Appearance, @@ -103,6 +106,9 @@ impl Viewport { grid_edge: usize, inspector: Option<&super::inspector::Inspector>, ) { + if self.authored_forces_pending { + self.mode = ViewMode::Appearance; + } ui.horizontal_wrapped(|ui| { for (mode, label) in [ (ViewMode::Appearance, "Appearance"), @@ -110,7 +116,9 @@ impl Viewport { (ViewMode::Obstacle, "Obstacle lab"), (ViewMode::Transport, "Transport session"), ] { - ui.selectable_value(&mut self.mode, mode, label); + ui.add_enabled_ui(!self.authored_forces_pending || mode == ViewMode::Appearance, |ui| { + ui.selectable_value(&mut self.mode, mode, label); + }).response.on_disabled_hover_text("Authored-force preview and lab runs are not integrated. Use the scene list and properties to author the draft."); } if self.mode != ViewMode::Appearance { ui.checkbox(&mut self.engineering_camera.orthographic, "Orthographic"); diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index dceb1fed..ef66c74b 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -52,6 +52,8 @@ pub mod run_sets; pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; +#[path = "desktop/scene_ui.rs"] +mod scene_ui; #[path = "desktop/settings.rs"] mod settings; #[cfg(test)] @@ -1241,6 +1243,7 @@ impl TimelineControls { } struct App { + scene: scene_ui::SceneUi, setup_workflow: SetupWorkflow, ai_comparison: Option, archive: archive_view::ArchiveView, @@ -1295,6 +1298,7 @@ impl Default for App { fn default() -> Self { let thread_limit = available_threads(); Self { + scene: scene_ui::SceneUi::default(), setup_workflow: SetupWorkflow::default(), ai_comparison: None, archive: archive_view::ArchiveView::default(), @@ -1439,22 +1443,31 @@ impl App { self.controls(ui); } }); - if let Some(action) = action { - match action { - SetupAction::Run(SetupWorkflow::Preview) => self.start(false), - SetupAction::Run(SetupWorkflow::AcceptedSession) => { - if let Err(error) = self.start_configured() { - self.status = error; - } - } - SetupAction::Cancel => self.cancel(), - } - ctx.request_repaint(); - } + self.dispatch_setup_action(ctx, action); }) .response .rect } + fn dispatch_setup_action(&mut self, ctx: &egui::Context, action: Option) { + if let Some(action) = action { + match action { + SetupAction::Run(SetupWorkflow::Preview) => self.start(false), + SetupAction::Run(SetupWorkflow::AcceptedSession) => { + if let Err(error) = self.start_configured() { + self.status = error; + } + } + SetupAction::Cancel => self.cancel(), + } + ctx.request_repaint(); + } + } + fn scene_run_strip(&mut self, ctx: &egui::Context) { + egui::TopBottomPanel::top("scene-global-run-strip").show(ctx, |ui| { + let action = self.run_controls(ui); + self.dispatch_setup_action(ctx, action); + }); + } fn run_controls(&mut self, ui: &mut egui::Ui) -> Option { let mut action = None; if let Some(worker) = &self.worker { @@ -1479,15 +1492,19 @@ impl App { let launch = match self.setup_workflow { SetupWorkflow::Preview => ui .add_enabled( - !self.closing, + !self.closing && self.scene.launch_readiness().is_ok(), egui::Button::new("Simulate") .min_size(egui::vec2(ui.available_width(), 32.0)), ) .clicked(), - SetupWorkflow::AcceptedSession => { - self.configured_controls.launch_button(ui, !self.closing) - } + SetupWorkflow::AcceptedSession => self + .configured_controls + .launch_button(ui, !self.closing && self.scene.launch_readiness().is_ok()), }; + if let Err(error) = self.scene.launch_readiness() { + ui.colored_label(egui::Color32::YELLOW, "Authored-force runs unavailable") + .on_hover_text(error); + } if launch { action = Some(SetupAction::Run(self.setup_workflow)); } @@ -1512,6 +1529,7 @@ impl App { action } fn reset_timeline(&mut self) { + self.scene.clear_run(); self.history = None; self.running_request = None; self.rows.clear(); @@ -1552,6 +1570,7 @@ impl App { Ok(limits) } fn start_configured(&mut self) -> Result<(), String> { + self.scene.launch_readiness()?; if self.worker.is_some() { return Err("Finish the active worker first".into()); } @@ -1736,6 +1755,10 @@ impl App { Ok((self.size, Some(config))) } fn start(&mut self, all: bool) { + if let Err(error) = self.scene.launch_readiness() { + self.status = error; + return; + } if self.worker.is_some() { return; } @@ -1880,6 +1903,7 @@ impl App { self.scheduler = Some(Arc::clone(&worker.scheduler)); self.history = Some(Arc::clone(&worker.history)); self.worker = Some(worker); + self.scene.begin_run(request.collision_mesh.is_some()); self.running_request = Some(request); self.rows.clear(); self.textures.clear(); @@ -2000,6 +2024,19 @@ impl App { } } } + /// Draft authoring never clamps or commits retained playback state. + fn scene_timeline_ui( + &mut self, + ctx: &egui::Context, + count: usize, + frame: Option<&PreviewFrame>, + ) -> Option { + if self.scene.context == scene_ui::SceneContext::Retained { + Some(self.timeline_ui(ctx, count, frame)) + } else { + None + } + } /// Paint playback only; frame/history/worker ownership stays with the caller. fn timeline_ui( &mut self, @@ -2086,7 +2123,16 @@ impl App { if configured { self.configured_controls.show(ui, self.worker.is_none()); } else { - if self.collision.show(ui, self.domain_side, self.material_mode) { self.reset_timeline(); } + let previous_mesh = self.collision.mesh.clone(); + if self.collision.show(ui, self.domain_side, self.material_mode) { + match self.scene.replace_model(self.collision.mesh.is_some()) { + Ok(()) => self.reset_timeline(), + Err(error) => { + self.collision.mesh = previous_mesh; + self.collision.status = format!("Import rejected; previous mesh retained: {error}"); + } + } + } egui::CollapsingHeader::new("Fluid material").default_open(true).show(ui, |ui| { ui.add_enabled_ui(self.material_mode,|ui|self.materials.fluid(ui)); }); @@ -2203,7 +2249,7 @@ impl App { ui.small("Fixed actual dt; a shortened step retains its archive and reports unsupported saved intent."); if ui.button("Compare both pressure solvers").clicked() {self.start(true);} } - if ui.add_enabled(self.worker.is_none(), egui::Button::new("Compare Rheon / trained AI")).clicked() { + if ui.add_enabled(self.worker.is_none() && self.scene.launch_readiness().is_ok(), egui::Button::new("Compare Rheon / trained AI")).clicked() { let mut comparison = ai_comparison::ComparisonApp::new(String::new(),std::env::current_dir().unwrap_or_default().join(".generated/ai-comparison")); comparison.set_steps(self.steps.clamp(1,512)); self.ai_comparison = Some(comparison); @@ -2373,17 +2419,50 @@ impl eframe::App for App { self.archive.show(ctx); return; } + if self.scene.context == scene_ui::SceneContext::Draft { + self.playing = false; + } if self.playing { self.play_time += self.playback_speed * ctx.input(|i| i.stable_dt.min(0.1)) as f64; } - let (count, frame) = self.selected_frame(); - self.sidebar(ctx); + if self.scene.open && ctx.content_rect().width() < 1100.0 { + self.scene_run_strip(ctx); + } else { + self.sidebar(ctx); + } if self.archive.active || self.run_sets.active || self.saved_comparison_active { ctx.request_repaint(); return; } - self.timeline_ui(ctx, count, frame.as_deref()); - let workers = if self.follow_live && self.show_workers { + let model_mesh = if self.scene.context == scene_ui::SceneContext::Retained { + self.running_request + .as_ref() + .and_then(|request| request.collision_mesh.as_ref()) + } else { + self.collision.mesh.as_ref() + }; + let model = model_mesh.map(|mesh| scene_ui::ModelSummary { + triangles: mesh.triangles().len(), + bounds: mesh.bounds(), + }); + let previous_context = self.scene.context; + self.scene.show( + ctx, + self.running_request.is_some(), + self.worker.is_some(), + model, + ); + if previous_context != self.scene.context { + self.playing = false; + } + let retained = self.scene.context == scene_ui::SceneContext::Retained; + let (count, selected) = if retained { + self.selected_frame() + } else { + (0, None) + }; + self.scene_timeline_ui(ctx, count, selected.as_deref()); + let workers = if retained && self.follow_live && self.show_workers { self.worker .as_ref() .and_then(|w| w.observer.as_ref()) @@ -2394,11 +2473,13 @@ impl eframe::App for App { let domain_side = self .running_request .as_ref() + .filter(|_| retained) .map_or(self.domain_side, |r| r.domain_side); self.viewport.domain_side = domain_side; let emitter = self .running_request .as_ref() + .filter(|_| retained) .map_or(self.cylinder.then_some(self.emitter), |r| r.emitter) .map(|mut e| { e.center = e.center.map(|v| v / domain_side); @@ -2412,9 +2493,19 @@ impl eframe::App for App { self.viewport.collision_mesh = self .running_request .as_ref() + .filter(|_| retained) .map_or_else(|| self.collision.mesh.clone(), |r| r.collision_mesh.clone()); - let grid = self.running_request.as_ref().map_or(self.size, |r| r.size) as usize; - let (_, frame) = self.selected_frame(); + let grid = self + .running_request + .as_ref() + .filter(|_| retained) + .map_or(self.size, |r| r.size) as usize; + self.viewport.authored_forces_pending = !retained && !self.scene.draft().objects.is_empty(); + let frame = if retained { + self.selected_frame().1 + } else { + None + }; egui::CentralPanel::default() .frame(egui::Frame::NONE) .show(ctx, |ui| { @@ -2424,7 +2515,10 @@ impl eframe::App for App { emitter, &workers, grid, - self.history.as_ref().map(|h| &h.inspector), + self.history + .as_ref() + .filter(|_| retained) + .map(|h| &h.inspector), ) }); if self.worker.is_some() || self.playing { @@ -2482,6 +2576,9 @@ fn main() -> eframe::Result { app.collision .apply(app.domain_side) .map_err(std::io::Error::other)?; + app.scene + .replace_model(true) + .map_err(std::io::Error::other)?; } if run { app.start(false); From 60f3498f19403fc449aa2d868a4fdf4238fa786e Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 23:23:36 +0000 Subject: [PATCH 10/25] Add shared viewport scene picking and thin selection outlines --- .../.gitattributes | 1 + .../viewport-selection-20261020/manifest.json | 69 ++ .../rheon-ui-picking-accepted-check.log | 2 + .../rheon-ui-picking-accepted-clippy.log | 2 + .../rheon-ui-picking-accepted-diff.log | 0 .../rheon-ui-picking-accepted-fmt.log | 0 ...ry_depth_near_clip_and_degenerate_hits.log | 8 + ...eserve_zero_direction_sign_and_support.log | 8 + ...ds_exact_seams_and_refuses_nonmanifold.log | 8 + ...erlap_retained_and_unavailable_preview.log | 8 + ...entity_ignores_drag_and_modified_click.log | 8 + .../rheon-ui-picking-list-final6.log | 187 +++ docs/plans/ui-refinement/execution-ledger.md | 20 + docs/plans/ui-refinement/plan.md | 14 +- .../reports/force-handles-admission.md | 58 + .../reports/force-handles-plan-review.md | 97 ++ .../native-viewport-selection-handoff.md | 32 + .../reports/viewport-selection-admission.md | 56 + .../reports/viewport-selection-plan-review.md | 93 ++ .../viewport-selection-source-review.md | 175 +++ src/bin/desktop/scene_ui.rs | 9 + src/bin/desktop/scene_view.rs | 1050 +++++++++++++++++ src/bin/desktop/viewport.rs | 62 + src/bin/rheon-desktop.rs | 24 +- 24 files changed, 1976 insertions(+), 15 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-check.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-diff.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-list-final6.log create mode 100644 docs/plans/ui-refinement/reports/force-handles-admission.md create mode 100644 docs/plans/ui-refinement/reports/force-handles-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/native-viewport-selection-handoff.md create mode 100644 docs/plans/ui-refinement/reports/viewport-selection-admission.md create mode 100644 docs/plans/ui-refinement/reports/viewport-selection-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/viewport-selection-source-review.md create mode 100644 src/bin/desktop/scene_view.rs diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/.gitattributes b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/manifest.json b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/manifest.json new file mode 100644 index 00000000..30a89042 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/manifest.json @@ -0,0 +1,69 @@ +{ + "parent": "6127cecbdcfe53270347e59ebe86ff3317528cfe", + "test_boundary": "individually exact inert UI/geometry only; no numerical/model runtime or native display", + "exact_test_names": [ + "scene_view::tests::geometry_depth_near_clip_and_degenerate_hits", + "scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support", + "scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold", + "scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview", + "scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click" + ], + "source_sha256": { + "src/bin/desktop/scene_view.rs": "d1ea804559495fe869b143383734afb953b0462e06b12e176840632ac003cb05", + "src/bin/desktop/scene_ui.rs": "0dd573b5bb2d7d9af3249a15ee768d5a39ce3381db010d04bccb7f6cee954d40", + "src/bin/desktop/viewport.rs": "c4fbdf394accc0b831460a3a1e75d78ee0dea900043bc82b2543619de4aa3a57", + "src/bin/rheon-desktop.rs": "af4c6ed921b2fde0a78d5bc2ebeb45f1a8f939b4947de82067e5f4278a537bbb" + }, + "logs": { + "rheon-ui-picking-list-final6.log": { + "sha256": "ecddbb71371c3fa42c1212379850d8f3290ae0333e3ba4cca011b7affd90b475", + "bytes": 19299, + "source": "/tmp/rheon-ui-picking-list-final6.log" + }, + "rheon-ui-picking-accepted-check.log": { + "sha256": "b8dd291b69b62c0719c18a5c4ae02c0c692d7e36311fa3a586fa4e4b92662e11", + "bytes": 130, + "source": "/tmp/rheon-ui-picking-accepted-check.log" + }, + "rheon-ui-picking-accepted-clippy.log": { + "sha256": "a691eaa7d6c704d296265d3d442ef64f1decc907608bdaed5c59d444129fe754", + "bytes": 130, + "source": "/tmp/rheon-ui-picking-accepted-clippy.log" + }, + "rheon-ui-picking-accepted-diff.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-picking-accepted-diff.log" + }, + "rheon-ui-picking-accepted-fmt.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-picking-accepted-fmt.log" + }, + "rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log": { + "sha256": "90970cd0c4c61470364a6701fd19a74be9c1e336c52ed5c0f8fe792fe023b992", + "bytes": 386, + "source": "/tmp/rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log" + }, + "rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log": { + "sha256": "86486951043707d8bc3e97bb159844ed1a93470a16bad3b815b19515e8285b29", + "bytes": 404, + "source": "/tmp/rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log" + }, + "rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log": { + "sha256": "4ab91747e4f8b19664509aaa05fcd30f63927da13a28f92c3b47e9c081949404", + "bytes": 410, + "source": "/tmp/rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log" + }, + "rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log": { + "sha256": "b772c3f70361a8c6a26058c7d25b668aa12c5a5c631a92f4a253cf82c7314c1a", + "bytes": 402, + "source": "/tmp/rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log" + }, + "rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log": { + "sha256": "c46a4224be5f1b0a47bfb817b1d81a8d5ba5a04c3efe0bd32c3ebfb059e80d3e", + "bytes": 409, + "source": "/tmp/rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log" + } + } +} diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-check.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-check.log new file mode 100644 index 00000000..ab8d7678 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-clippy.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-clippy.log new file mode 100644 index 00000000..b766dd41 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.76s diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-diff.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-diff.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-fmt.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log new file mode 100644 index 00000000..2850da77 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_depth_near_clip_and_degenerate_hits.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_depth_near_clip_and_degenerate_hits ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 181 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log new file mode 100644 index 00000000..caacbb6f --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 181 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log new file mode 100644 index 00000000..ffa671c7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.22s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 181 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log new file mode 100644 index 00000000..aaa0b601 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pick_identity_overlap_retained_and_unavailable_preview.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.24s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 181 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log new file mode 100644 index 00000000..adf34e8b --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-accepted-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 181 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-list-final6.log b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-list-final6.log new file mode 100644 index 00000000..4409f41e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-selection-20261020/rheon-ui-picking-list-final6.log @@ -0,0 +1,187 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 10.59s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +182 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 15ccfd24..11ec7a2b 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -339,3 +339,23 @@ Active progress/Cancel, model rollback/native, actual viewport mode guard and whole-app focus/AT behavior remain unverified. Prior incident teardown/scientific evidence limits and parked live cancellation are unchanged. + +## T3 viewport selection source checkpoint —2026-10-20 + +Follows immutable6127cec authoring checkpoint; published separately on PR146. +One canonical SceneUi selection now drives x-ray force markers/support strokes, +model triangle picking, selected thin outline and properties. Exact-position welded, +winding-independent model silhouette excludes triangulation seams; non-manifold +outline and overbudget/unrepresentable projection refuse explicitly. Invalid misses +retain correction selection. Zero fields remain neutral, signed nonzero markers use +canonical transform/sign. Force glyphs are authoring geometry, not magnitude samples. + +Five inspected names listed from182 total, each executed with full --exact and +single test thread. Final five each passed. Formatting, strict production Clippy, +locked offline desktop check and diff checks passed. Raw logs and four source seals: +`evidence/viewport-selection-20261020/manifest.json`. Earlier intermediate successful +runs remain under /tmp/rheon-ui-picking-*; no numerical fixture or native test ran. +Independent plan/source reports record all corrections. No DISPLAY/WAYLAND_DISPLAY; +new exact revision requires native render/pick/focus/high-DPI evidence separately. +Runtime/archive force readiness still refuses; T4 handle plan reviewed, not accepted +as implemented. Original M2a/precision and incident evidence remain unchanged. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 302c6eb1..3c4e2660 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T2 authoring source gates complete; native validation pending -**Next slice:** T3 viewport picking and selected-object outline +**Current phase:** T3 source checkpoint;6127 native validation separate +**Next slice:** T4 force translation/rotation/support handles **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -36,7 +36,9 @@ M1 remains incompletely accepted; old native evidence is scoped to426fd25 only. Canonical admission is this path, operation `continue`. Sole desktop UI owner: this task. A separate task owns the shared force domain and both solver adapters. Only independently owned UI work can begin before its exact API is reconciled. -No optional approval is needed for the reversible T1 slice after plan review. +No optional approval is needed for admitted reversible UI slices after review. +Parent authorizes distinct commits, normal pushes and draft PR146 publication; +no merge, force push or branch deletion. ## Source, standards and preservation @@ -68,7 +70,7 @@ IDs. T5 wires qualified immutable runtime/archive paths and native evidence. No competing force physics, backend-specific UI approximations, solver/model/ scientific execution, unadmitted archive migration, worker/session ownership change, training, -L1/MMS work, dependency/framework rewrite, publication, PR/merge or cleanup. +L1/MMS work, dependency/framework rewrite, merge, force push or cleanup. Live cancellation stays parked on missing existing containment; no provisioning. Canonical domain source is now received and sealed. No fabricated runtime support while desktop dispatch/frozen intent/load/rerun integration remains pending. @@ -94,8 +96,8 @@ Inspector slice before any overlapping changes; T1 excludes Inspector entirely. | --- | --- | --- | --- | | T1 | main timeline presentation and desktop/timeline_ui_tests.rs; bounded labelled speed editor, ordinary invisible local measurement, unchanged physical context | Four inspected exact UI probes + compile/Clippy/fmt + independent source review; native remains separate | Completed (bounded source) | | T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests; viewport.rs mode-control guard only (no lab dispatch/picking changes) | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; viewport mode refusal only, no runtime dispatch | Completed (bounded source) | -| T3 | viewport.rs picking and thin actual-geometry outline; scene_ui.rs same selection API; main projection routing | T2 source gates, reviewed exclusive pick/camera/probe policy; inert projection tests then native | Planned | -| T4 | desktop/scene_manipulator.rs, viewport gesture routing, scene_ui validated transform intent and collision_ui transactional model placement | Same IDs/domain capabilities; no duplicate force physics; Escape rollback, explicit model Apply | Planned | +| T3 | new scene_view.rs geometry/picking; viewport.rs aggregate-input composition and thin geometry outline; existing SceneUi selection and main routing | [Exact selection admission](reports/viewport-selection-admission.md), inert camera/geometry/UI tests; no lab initialization, then native | Bounded source complete; native pending | +| T4 | desktop/scene_handles.rs, viewport exclusive gesture routing, scene_ui validated draft transaction; force-only (model placement deferred) | [Force-only handle admission](reports/force-handles-admission.md); same IDs/domain types; no physics; exact Escape rollback and terminal-frame ownership | Planned | | T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | diff --git a/docs/plans/ui-refinement/reports/force-handles-admission.md b/docs/plans/ui-refinement/reports/force-handles-admission.md new file mode 100644 index 00000000..2bf28dc1 --- /dev/null +++ b/docs/plans/ui-refinement/reports/force-handles-admission.md @@ -0,0 +1,58 @@ +# T4 force manipulation admission + +Sequential continuation of T3 on PR146; same sole desktop source owner. No core, +solver, recording, Request or run-set edits. Files: scene_ui.rs transactional draft +edit, new scene_handles.rs geometry/capture; scene_view.rs helpers/composition; +viewport.rs ownership before camera; main supplies current editability. Literal +UI/geometry tests only, inspected then list-only then individual exact names. + +Use the canonical ForceObject/ForceTransform/ForceSupport. One SceneUi selection. +The canonical draft is unchanged while dragging: keep one validated candidate +and exact base object in a SceneUi edit transaction. Display candidate as an +explicit preview; release commits once only if base/selection/context are still +current. Escape, focus loss, pointer loss, identity/context/geometry change cancel. +Freeze before accepting candidate; never normalize a malformed stored quaternion, +clamp support dimensions silently, or change run snapshots. Properties/add/remove +are disabled during capture. Numeric properties remain the keyboard alternative. + +Named Translate / Rotate / Support controls apply to the selected Draft force +only, idle worker only. Models remain read-only until an accepted model-placement +transaction bridge exists. Retained context shows no current draft glyph/handles. +Translation axes are world XYZ; rotation and support axes are local XYZ. Colour +is accompanied by X/Y/Z labels and hover instructions. Global has no size handle. +Dragging a Global Directional anchor only changes authoring placement, not the +uniform field; explain this in contextual help. All physical distances remain m. + +Projection shares the existing camera/near/viewport finite checks. Begin on an +unmodified primary press over a visible selected handle before picking/camera. +Capture exclusively owns pointer until release, including outside canvas. Escape +cancels and latches ownership until release; do not hand an ongoing drag to camera. +Other buttons/modifiers begin camera normally; changing modifiers during captured +gesture does not retarget its axes. Wheel/Home/zoom ignored while captured. Focus +canvas on begin; never steal keyboard focus without a click. Cancel when window +focus, canvas rect/camera/domain, selection/context or source object changes. + +Translation uses closest ray/axis parameters; near-parallel rays refuse admission. +Rotation uses ray/plane vectors about the chosen local axis, atan2 signed angle, +then world-axis unit quaternion multiplication against exact original orientation. +Near-parallel/central-ray degeneracy refuses. Support-size uses local-axis ray +displacement on original positive radius/half-extent; invalid candidates remain +uncommitted and visibly refused. Sphere uses its radius; Box uses each half-extent. +No arbitrary strength, density or force evaluation is performed by handles. + +Acceptance: pure independent axis/ray/quaternion cases plus actual local egui +pointer press/drag/release, Escape latch, modified-button admission, camera-owner +return, focus/pointer/context/identity/rect loss, invalid support rejection and +snapshot/base bit preservation. Compile/Clippy/fmt, independent source review; +then native exact-revision high-DPI hit/contrast/focus tests by existing validator. +No native/assistive usability, model manipulation, runtime/archive or scientific +acceptance is implied by headless Context tests. Rollback only this distinct +increment, preserving T3/6127 evidence and unrelated M2a branches. + +Independent review conditions: current-pointer validity is separate from last +validated preview; valid move then invalid move then release cancels, never commits +stale preview. Every cancellation reason latches ownership until button release. +The terminal release/cancel frame remains consumed even after transaction removal. +A hit but geometrically unsolvable handle visibly refuses and captures/latches, +never silently begins camera orbit. Quaternion evidence uses nonidentity base and +local axis to distinguish multiplication order. diff --git a/docs/plans/ui-refinement/reports/force-handles-plan-review.md b/docs/plans/ui-refinement/reports/force-handles-plan-review.md new file mode 100644 index 00000000..a3967dea --- /dev/null +++ b/docs/plans/ui-refinement/reports/force-handles-plan-review.md @@ -0,0 +1,97 @@ +# Independent T4 force-handle plan review + +## Disposition + +Conditionally suitable for the next sequential production slice. The canonical +`ForceObject` transaction, force-only scope, explicit unsupported model handling +and independent gesture ownership are proportionate. Resolve the conditions +below in the admission/canonical plan before implementation; verify them in the +source review and exact UI tests before calling T4 implemented. No new runtime, +solver, recording or model-placement work is required by this review. + +## Reviewed evidence and limits + +- Worktree: `/workspace/Rheon-ui-authoring`; HEAD + `6127cecbdcfe53270347e59ebe86ff3317528cfe`. +- T3 source was locally modified and uncommitted at review. T4 depends on its + distinct reviewed checkpoint, rather than treating the current tree as a + committed T3 base. +- Admission: `reports/force-handles-admission.md`, SHA-256 + `f15cbb62e1886d4d2dc04ff2a4321b9cf700a1dc2a034fb06a86ad7e9a260d4b`. +- Canonical plan: `plan.md`, SHA-256 + `ba9fddff4fee2982af4098ad4fcee07f8081f69746f48f87fcd552068dcbf962`. +- Read the canonical force schema/validation, `scene_ui.rs` ownership, + `scene_view.rs` selection/projection, `viewport.rs` input routing and + `camera.rs` projection. This is a source/design review, with no builds, tests, + application interaction, screenshots or scientific execution. +- Existing standards source review applies; this report is not Standards Engine + certification. Native focus, high-DPI hit targets and contrast remain for the + exact-revision native validator. + +## Required design conditions + +| Priority | Finding | Required disposition and evidence | +| --- | --- | --- | +| High | A last-valid preview alone cannot represent an invalid current pointer position. A valid size move followed by crossing zero or ray degeneracy could otherwise commit an obsolete preview on release. | Keep current-position validity separately from the candidate. Invalid release rejects/discards the transaction; canonical draft retains its exact base. Test valid move → invalid move → release, including support and degenerate geometry. | +| High | Gesture cancellation/commit clears transaction state, but the same frame can still reach T3 click selection or camera input. Escape alone is not the only cancellation route. | Return explicit per-frame input consumption. Every cancellation route retains ownership until primary release, and terminal commit/cancel/release consumes that frame. Test Escape, focus/pointer loss, context/identity/rect/camera/domain changes and no fall-through selection or camera movement. | +| Medium | The canonical milestone still names `scene_manipulator.rs` and a `collision_ui` model-placement bridge; the new admission is narrower. | After the separate T3 checkpoint, make the canonical next slice the exact force-only write set: `scene_handles.rs`, canonical transaction in `scene_ui.rs`, projection/composition helpers, viewport routing and main editability. Defer model movement/Apply explicitly; do not touch the bridge in this slice. | +| Medium | A visible handle can be hit even when its axis/plane is geometrically unsolvable. Falling through to orbit would contradict the visible manipulation affordance. | Distinguish handle hit from solvable capture. A hit with near-parallel/central-ray refusal reports concise feedback and consumes/latches that attempt. A genuine miss and initially modified/nonprimary press retain ordinary camera behavior. | + +These conditions were sent to the implementation owner before production T4 +edits. They are design requirements, not observed defects in an implementation +that has not yet been written. + +## Transaction and geometry contract + +`SceneUi` remains the single owner of draft, selection and edit transaction. Keep +the original canonical object and a candidate, without writing the draft during +drag. On release recheck Draft context, idle worker, selected identity, unchanged +source object and captured geometry. Freeze the candidate in the containing +scene before the one canonical replacement. On conflicts, discard the preview; +never restore an old base over a newer external edit or selection. Unrelated +objects, IDs, kind, units, activation times, enabled state and snapshot remain +unchanged. Numeric properties, add/remove and mode changes cannot mutate a +captured transaction behind the handle. + +Derive every update from the original object and initial ray parameter/vector, +rather than accumulating frame deltas. Invert the actual perspective and +orthographic camera projection; convert normalized domain coordinates to metres +exactly once. Require finite values, valid domain/rect/camera and forward/near +intersection. Translation uses world axes. Rotation and support use the base +orientation's local axes; apply the world-axis delta quaternion to the original +orientation in the correct order. Do not repair malformed stored orientations or +silently clamp invalid dimensions. Global support has no size handle; sphere +radius and box half-extents retain their canonical meanings and SI units. + +Capture starts only on a plain primary press over a selected editable force +handle, before camera input and object picking. Keep it while held outside the +canvas. Focus/pointer loss is cancellation, never synthetic release acceptance. +Do not steal focus without that initiating click. Block wheel, zoom, rotation +events and camera keys while owned; mid-drag modifiers do not change mode/axis. +Models, Retained context and active workers do not offer editable handles. Named +modes/axes and numeric properties remain available for discoverability and a +keyboard alternative. The Global Directional placement limitation must remain +in contextual help. + +## Proportionate acceptance evidence + +Inspect test bodies/helper calls before list-only enumeration, then invoke only +individual fully qualified exact tests. Use literal geometry and local egui +contexts; no Worker, solver/evaluator, runtime fixtures or imported model files. + +- Independent ray/axis and ray/plane cases in perspective and orthographic + projection, with finite/near/parallel rejection and positive SI sizes. +- A nonidentity original quaternion and nontrivial local axis/angle to distinguish + quaternion multiplication order; original-based repeat updates to expose drift. +- Real press/move/release showing canonical draft unchanged through preview and + changed once on valid release; invalid-current release leaves base bits intact. +- Terminal-frame consumption and until-release cancellation latch, including + modified/nonprimary admission, outside-canvas continuation, focus/pointer loss, + stale identity/context/source and camera/rect/domain change. Ordinary camera + navigation resumes only for a later independent gesture. +- Draft/Retained/worker/model guard cases; immutable snapshot and unrelated field + preservation. These checks exercise metadata validation, not field evaluation. + +Compile/lint/format and an independent source review remain implementation gates. +Headless evidence cannot close native usability acceptance. Roll back only the +distinct T4 increment; preserve T3, `6127` native evidence and unrelated branches. diff --git a/docs/plans/ui-refinement/reports/native-viewport-selection-handoff.md b/docs/plans/ui-refinement/reports/native-viewport-selection-handoff.md new file mode 100644 index 00000000..bf655068 --- /dev/null +++ b/docs/plans/ui-refinement/reports/native-viewport-selection-handoff.md @@ -0,0 +1,32 @@ +# Native T3 viewport selection handoff + +Use the published T3 checkpoint on PR146, verify exact commit/tree and build seals +before testing. Preserve the separate6127cec authoring evidence. Idle authoring +only: no Run, comparison, archive rerun, numerical lab or model workload. Existing +accepted mesh fixture may be reused only under the validator's existing permission; +if unavailable, mark actual model selection/outline unverified. No new mesh import +or solver is necessary for the force checks. + +At1250×820 and900×650,100% and150% scale: open Scene, add each zero force and use +tree selection to identify overlapping origin anchors. Confirm neutral zero glyphs, +name/selection outline, disabled force selection, empty click clearing, tree ↔ +viewport properties, renamed/long names and collapse retention. Set nonzero signed +radial/vortex and directional values through properties; inspect fixed-size signed +orientation and actual metre support wires with rotation. Glyphs are x-ray aids, +not field samples/magnitude. Verify click vs camera drag/Shift-pan/Ctrl-zoom/middle, +wheel concurrent with click, Home, keyboard focus and no unintended property edits. +Selection should open the property panel. Retained view, if an already authorized +fixture exists, must show no current draft forces and select immutable model only. + +A selected accepted model has a1.35logical-pixel x-ray silhouette/boundary outline, +not all triangulation edges; duplicated seams and reversed triangle winding should +not introduce internal diagonals. Above12000triangles selection explicitly refuses; +non-manifold edges refuse only outline while established surface misses still clear. +Coordinates outside±1e12 domain lengths explicitly refuse display, never change +physical values. Invalid draft/support/rotation displays a warning below measured +viewport information; unsuccessful preview miss preserves correction selection. + +Capture actual rendered quality85JPEGs with exact revision, window size/scale and +state labels. Record Library identities only after confirmed upload. This task has +no DISPLAY/WAYLAND_DISPLAY and claims no native rendering, screenshot, high-DPI, +accessibility or gesture usability pass. Existing runtime refusal remains active. diff --git a/docs/plans/ui-refinement/reports/viewport-selection-admission.md b/docs/plans/ui-refinement/reports/viewport-selection-admission.md new file mode 100644 index 00000000..9602fb4b --- /dev/null +++ b/docs/plans/ui-refinement/reports/viewport-selection-admission.md @@ -0,0 +1,56 @@ +# T3 viewport selection admission + +Base6127cecbdcfe53270347e59ebe86ff3317528cfe on PR146. Preserve that exact commit, +evidence and native handoff. Same sole UI owner and normal-push branch. + +Write set: new desktop/scene_view.rs pure authoring geometry/projected hit tests; +viewport.rs composition of scene interaction/paint plus small aggregate view-input +API; main composition passes the existing SceneUi by mutable reference. SceneUi +selection API remains canonical; optional force type label/help only. No core +force/evaluator/solver/recording/runtime or model import change. + +Force markers display canonical translation/rotation/support, with fixed-size +visual directional/radial/vortex glyphs. Marker size does not claim physical force +magnitude; Global anchors do not imply bounded physical support. Sphere/Box wire +support uses actual metres and local transform. Disabled objects remain visible, +muted and selectable. Invalid drafts refuse force preview with visible feedback; +never silently normalize malformed transforms or infer historical forces. + +The one accepted model uses actual triangle geometry, not a bounding-box pick. +Selected outline is thin screen-space silhouette/boundary edges, not every mesh +triangulation edge. Force wire/glyph selection likewise follows rendered geometry. +Bound projection to256 forces and12000 model triangles; reject unsupported display +instead of silently reducing scene selection. Display coordinates outside ±1e12 domain lengths explicitly refuse (no physical +value changes). Check finite inputs, near-plane +clipping, degenerate projected triangles/segments, perspective/orthographic depth, +viewport bounds and deterministic overlap/tie behavior. + +Plain primary click chooses closest authoring stroke/marker (then depth/stable ID), +otherwise nearest model surface. Markers are explicitly x-ray authoring overlays, +selectable through fluid/model; models are surface-picked. Empty click clears +selection. Camera drags and modified/middle gestures do not select. Never replay +App.update or initialize a numerical lab to test it. Tree and viewport both call +SceneUi.select; missing IDs clear and context changes keep their established rules. +Retained view may select actual frozen model geometry read-only; it never paints +current draft forces. Existing particle probes keep their separate identity/mode. + +T4 follows with force translation/rotation/support handles. Its captured gesture +must precede picking/camera and commit one validated draft edit on release, with +Escape/focus/context/identity loss cancelling exactly. This slice does not claim +handles or authored runtime. New force runs remain refused. + +Evidence: inspect full geometry/UI helper paths, list-only enumerate, then individually +selected --exact geometry/UI tests using literal arrays/force documents, camera and +local Context only. No CollisionMesh construction/import, numerical/model/Worker, +history/recording or filesystem fixture/cleanup. Compile/Clippy/fmt and independent +source review before commit/normal push. Native current6127 validation remains +separate; new revision needs its own render/pick/outline evidence and quality85JPEGs. + +Independent review conditions resolved in source: invalid/nonrepresentable projection +has a distinct unavailable miss and keeps selection; valid hits still select. +Exact-position welding removes duplicate-index seams, and canonical edge/opposite +vertex projection makes two-sided outlines independent of triangle winding. +Non-manifold edges explicitly refuse the outline (surface picking remains), and +these contours are x-ray silhouette/boundary overlays, including self-occluded edges; +this is not a depth-buffer outer contour claim. Zero strengths have only neutral +anchors/support. Signed radial arrows and vortex direction follow canonical sign. diff --git a/docs/plans/ui-refinement/reports/viewport-selection-plan-review.md b/docs/plans/ui-refinement/reports/viewport-selection-plan-review.md new file mode 100644 index 00000000..7a2ebe2e --- /dev/null +++ b/docs/plans/ui-refinement/reports/viewport-selection-plan-review.md @@ -0,0 +1,93 @@ +# Independent T3 viewport-selection plan review + +Reviewed 2026-10-20 by `ui_plan_review` against +`6127cecbdcfe53270347e59ebe86ff3317528cfe` in +`/workspace/Rheon-ui-authoring`. Initial reviewed plan SHA256: +`799d783f20fbc9469970c9a61de003badefcc58909a52a2f53766ddd36e7d47e`; +viewport-selection admission SHA256: +`b9b59a222715cd902f15c0b1b2d6e4541bde2fe525de56c80e3386ed9cc290f5`. + +Read the actual SceneUi selection/context API, viewport camera/input/render +composition, main draft/retained geometry bridge and accepted model topology +contract. Only this review report was authored. No build, test, GUI, solver, +model, evaluator, import or filesystem fixture was executed. + +## Disposition + +T3 is an appropriate bounded next slice. One pure projected-geometry module can +own authoring strokes, triangle picking and selected outlines while the existing +SceneUi remains the sole selection owner. The aggregate viewport input boundary +is proportionate to the existing composition. Neither force runtime nor its +recording propagation is needed to implement this authoring interaction. + +Incorporate the three concrete conditions below before claiming the complete T3 +selection/outline contract. They require no competing physics implementation, +new framework, broader worktree edits or optional user approval. Preserve the +exact 6127 source/native handoff; current native validation does not cover future +T3 source. Handles remain a subsequent separately admitted slice. + +## Findings and design conditions + +| ID | Severity | Condition | +| --- | --- | --- | +| VS1 | Medium | Distinguish unavailable force projection from an empty scene/ordinary miss. An invalid or unrepresentable draft must emit visible preview feedback, preserve canonical values and keep selection available for correction. Do not silently turn validation/projection failure into an empty force list that makes an ordinary empty click clear the selected invalid object. Model projection may have its own independently valid result. | +| VS2 | Medium | State and test the outline topology policy for accepted `TwoSidedSurface` models. That core policy permits open/disconnected surfaces, mixed winding and duplicate-position seams; only convex solids receive the stricter indexed-topology validation. Index-only boundary counting can expose coplanar triangulation seams as an outline. Support literal cases or explicitly refuse unsupported outline topology with feedback, without altering accepted collision geometry or silently drawing all triangle edges. | +| VS3 | Medium | Zero vector/strength is the default new-force state. Do not normalize zero or choose an invented direction. Use a neutral named anchor/type glyph for zero; nonzero schematic glyphs must preserve canonical directional orientation and radial/vortex sign plus rotation. Their fixed visual extent is not physical magnitude or bounded Global support. | + +These conditions refine the already admitted validation, geometry-following +outline and truthful schematic-glyph behavior; they do not authorize force +evaluation or change any domain semantics. + +## Camera, interaction and geometry contract + +- Current `OrbitCamera` projects domain-normalized coordinates, with near depth + 0.015 and screen scale based on viewport height. Scene data is in metres. + Normalize once at the projection boundary; match perspective/orthographic + conventions and clip projected strokes/triangles consistently. Do not silently + normalize malformed quaternions or accept nonfinite/narrowing-overflow data. +- Perspective model/stroke hit depth must be perspective-correct; screen-linear + interpolation of camera depth is not generally correct. Orthographic depth is + affine. Literal camera/triangle cases provide an independent expected nearest + surface without constructing CollisionMesh or sampling a force field. +- Picking and painting use the same current camera, rect, scene context and + canonical object IDs. Apply camera input before producing that frame's + projection, or otherwise keep one consistent immutable view projection. + Do not hit-test a stale cached projection against newly painted geometry. +- The named x-ray overlay policy is explicit: closest authoring stroke/marker + takes precedence, then its depth and stable ID; absent an overlay hit, use the + nearest actual model triangle surface. Boundary/tie thresholds need consistent + screen-space meaning. Empty click clears only a supported, established miss. +- Use unmodified primary click only. A completed camera drag, modified primary, + middle-button or wheel navigation must not also select/clear an object. + T4 will add exclusive handle capture before these operations; this slice does + not add a second gesture owner or modify recorded-probe identity. +- SceneUi.select validates the same object key used by tree/properties. Keep + retained-run model identity/geometry read-only, and never render current draft + forces as historical run facts. Current particle probes remain in their + separate mode; pure T3 tests must not initialize Obstacle/Transport labs. + +Projection work is bounded to the declared 256 forces and 12,000 model triangles. +Wire-support tessellation is render-only geometry, with explicit finite bounds +and display failure. Do not silently subsample pickable geometry or mutate a +canonical support to fit display limits. A thin screen-space selected stroke +must accompany the named selection in tree/properties, rather than using colour +as the only selection signal. + +## Focused evidence and remaining gates + +Inspect complete helper paths, enumerate list-only and execute one explicitly +allowlisted `--exact` pure geometry/UI test per command. Use literal arrays, +canonical force documents and local camera/Context/input only. Required cases +cover actual triangle versus bounding-box miss, front/back depth, perspective +and orthographic overlap, near-plane clipping, degenerate/offscreen geometry, +deterministic ties, valid two-sided seams or explicit topology refusal, zero and +signed glyphs, invalid preview, disabled-object selection and drag/modifier +non-selection. Real egui click tests connect the projection to SceneUi.select. + +Do not invoke App.update, CollisionMesh construction/import, numerical lab +initialization, history/recording, Worker or the force evaluator. Compilation, +strict Clippy/formatting and independent source review are supporting gates. +These tests do not prove native hit-target usability, outline contrast/width, +high-DPI focus or complete assistive access. Exact T3 source needs its own native +render/pick/outline evidence; no runtime/physics acceptance or publication result +is implied by this plan review. diff --git a/docs/plans/ui-refinement/reports/viewport-selection-source-review.md b/docs/plans/ui-refinement/reports/viewport-selection-source-review.md new file mode 100644 index 00000000..5dd921df --- /dev/null +++ b/docs/plans/ui-refinement/reports/viewport-selection-source-review.md @@ -0,0 +1,175 @@ +# Independent T3 viewport selection source review + +2026-10-20. Reviewed the uncommitted T3 increment in +`/workspace/Rheon-ui-authoring` against `viewport-selection-admission.md` and its +independent plan review, preserving parent +`6127cecbdcfe53270347e59ebe86ff3317528cfe`. This report is the reviewer's only +write. No tests, builds, native interaction, model import/construction, numerical +or evaluator workload, runtime/history/recording, filesystem fixture or cleanup +was executed by the reviewer. + +## Complete inert boundary and exact admission + +Read complete `scene_view.rs`, SceneUi's test-only `geometry_fixture`, the +Viewport/ViewInputs composition, main bridge and shared OrbitCamera paths. +The following five fully qualified names are the inspected exact probe boundary: + +- `scene_view::tests::geometry_depth_near_clip_and_degenerate_hits` +- `scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold` +- `scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support` +- `scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview` +- `scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click` + +Tests construct literal vertex/index arrays, force documents, projection points, +camera fields and a local egui Context. `geometry_fixture` stores those values +with a test-only model identity; it neither constructs CollisionMesh nor imports +geometry, allocates a worker, loads fields or performs production ID admission. +Projection calls ForceScene::freeze for validation/deep-copy only, never force +sampling/acceleration. Glyph construction is schematic UI geometry, not field +evaluation. Camera trigonometry, clipping, interpolation, BTreeMap topology, +shape painting and hit tests are local geometry arithmetic. The pointer test +allocates one egui response and calls only scene_view interact/paint. No App +default/update, viewport show, lab initialization, solver, history, Inspector, +recording, process, file or cleanup path is reached. Later added assertions on +these same literal paths do not expand the workload boundary. No broad test +filter is admitted by this source review. + +## Canonical selection and rendering semantics + +SceneUi remains the one selection/context owner. The main bridge supplies that +same mutable owner through ViewInputs, along with already selected frame/emitter/ +worker/Inspector projections. It does not copy canonical force properties or +change Request/solver/recording/run-set ownership. Appearance picking invokes +SceneUi.select; retained context excludes current draft forces and uses the +existing actual frozen model identity/geometry. Particle probes stay in their +separate mode. A supported authoring hit opens the existing properties drawer. + +The helper converts metres to domain-normalized display coordinates once per +projected point, applies the canonical quaternion without normalization and +validates the complete draft before force projection. Sphere/box wires use actual +support dimensions and transforms. Global forces receive an anchor/glyph without +inventing bounded support. Glyph extent is a fixed authoring scale, unrelated to +strength or density; its projection changes with camera. Zero directional/radial/ +vortex values have only neutral anchor/support. Nonzero directional orientation, +radial attraction/repulsion and right-handed local-Y vortex sign are preserved; +disabled objects remain muted/selectable. No authored force is executed. + +Projected support/glyph strokes are explicit x-ray overlays. Picks prioritize +closest stroke/anchor, then perspective-correct depth and stable ID; only without +an overlay hit do actual model triangle surfaces compete by nearest depth. +Unmodified primary clicks select or clear established misses. Dragged and modified +primary responses do not select. Middle/wheel navigation retains its camera +owner; manipulator capture/transactions are a later slice, not implemented here. +Camera input is now applied before picking and painting, so both use the same +current camera rather than picking old geometry then painting a changed view. + +## Geometry and topology review + +Near-plane clipping uses the existing camera convention with a small positive +margin. Screen-line clipping bounds strokes before tessellation. Stroke/triangle +depth interpolation is reciprocal in perspective and affine in orthographic +projection. Degenerate projected triangles do not produce surface hits. The +model uses its triangle inventory, not a bounding-box proxy; there is no silent +subsampling of selectable surfaces. + +Exact-position keys weld duplicate-index seams, including equivalent signed +zeros, without welding nearby physical vertices. Each shared edge is given one +canonical endpoint order; the opposite vertex's view-side sign is independent +of triangle winding. Opposite signs remove internal coplanar diagonals; boundary +and same-side contour edges remain. This supports open/disconnected/two-sided +input without treating its original winding as an outward-solid convention. +More than two incident faces explicitly refuses the outline while retaining +complete surface picking. The resulting thin contour is a silhouette/boundary +x-ray overlay and may include self-occluded edges; it is not a depth-buffer-only +outer-contour claim. Named tree/properties selection remains the complementary +non-colour signal. + +Work is bounded by backend validation to 256 forces and by display admission to +12,000 model triangles. A display-only normalized-coordinate limit of 1e12 now +refuses extreme projection explicitly; it never clamps or edits physical values. +Malformed/absent/over-budget geometry marks picking incomplete. Valid hits can +still select independently supported objects; uncertain misses preserve the +selection needed for correction. + +## Findings and repairs + +1. **Repaired:** Original viewport composition picked before camera input but + painted after it. Appearance input now precedes interaction and painting. +2. **Repaired:** An anchor outside the viewport could still be picked within + seven pixels although painting suppressed it. Picking now uses the same + centre-containment policy as painting; a literal edge case is included. +3. **Repaired:** Any warning originally made a miss unavailable, so an outline-only + non-manifold warning prevented clearing selection despite complete surface + projection. Dedicated `pick_incomplete` distinguishes that case from actual + invalid/unavailable projection, with an established-miss assertion. +4. **Repaired:** A finite but enormous coordinate could overflow screen projection + without warning and clear selection. The explicit display-coordinate refusal + covers the reported orthographic zero-Global example without touching its + canonical draft; an added literal assertion covers the extreme point. +5. **Repaired:** A hardcoded warning y=70 could overlap the width-wrapped viewport + and mesh captions. PaintArea now carries the measured bottom of that stack; + standalone tests use a Rect-derived area and do not claim integration layout + evidence for that stack. +6. **Repaired:** Nonfinite camera fields originally resembled ordinary offscreen + geometry and could clear selection on a miss. Projection now validates finite, + positive bounded canvas dimensions, finite camera distance/basis and unit basis + axes before any geometry. Failure produces visible unavailable feedback and + incomplete picking; legitimate behind-near/offscreen misses remain distinct. + A NaN-yaw literal assertion extends the existing inspected exact test. + +No native evidence exists for this new increment. Initial/final implementation +test logs, source seals and any final correction are to be recorded below before +final source disposition. Native hit-target usability, overlap/contrast, high-DPI +focus, model contour appearance, retained geometry and assistive access remain +separate requirements. Core force/runtime/scientific, archive provenance and +future handles are outside T3 acceptance. + +## Final disposition and sealed evidence + +No blocking source finding remains for the bounded T3 increment after the six +corrections above. This supports a source/focused-geometry checkpoint, not native +or full application interaction acceptance. The late camera/canvas assertion +remains on the same literal Projection helper path; no additional workload path +was introduced. The property drawer and SceneUi selection owner are reused; +there is no competing authoring state or new runtime dispatch. + +Independently read and hash-verified +`../evidence/viewport-selection-20261020/manifest.json`, SHA256 +`7dbed249419d616ae97017b3db272d2e1d10a0a3023f46ba1e463b5b536799cd`. +All four declared source seals match the current files. All ten retained logs +match declared sizes/SHA256 and their original `/tmp` bytes. The retained log +attribute disables text conversion. The five distinct exact test identities +match the list-only 182-test enumeration once each and each final result reports +one pass, zero failures/ignored and 181 filtered out. These logs were read, not +rerun by the reviewer. + +The final five probes cover depth/near clipping and degeneracy, winding/seam/ +non-manifold policy, zero and signed/transformed glyphs/support, triangle-vs-box +misses/overlap/disabled/retained/unavailable identity, off-canvas anchor refusal, +and real egui click/modified-click/drag-to-selection behavior. The malformed +camera and extreme orthographic coordinate assertions are included in the final +accepted identity probe. They are literal display geometry, not scientific +fixtures or force evaluator tests. + +The retained accepted check and Clippy logs complete successfully in 1.28 and +2.76 seconds respectively. Format/diff logs are empty, consistent with the +implementation owner's successful-exit receipt; empty output alone does not +encode an exit status. Reviewer independently ran read/hash verification and +`git diff --check`, which was clean, and executed no test or build. + +| Final source | SHA256 | +| --- | --- | +| `src/bin/desktop/scene_view.rs` | `d1ea804559495fe869b143383734afb953b0462e06b12e176840632ac003cb05` | +| `src/bin/desktop/scene_ui.rs` | `0dd573b5bb2d7d9af3249a15ee768d5a39ce3381db010d04bccb7f6cee954d40` | +| `src/bin/desktop/viewport.rs` | `c4fbdf394accc0b831460a3a1e75d78ee0dea900043bc82b2543619de4aa3a57` | +| `src/bin/rheon-desktop.rs` | `af4c6ed921b2fde0a78d5bc2ebeb45f1a8f939b4947de82067e5f4278a537bbb` | + +The pure pointer fixture calls interact/paint directly; it does not exercise +actual Viewport camera-input composition, particle-probe switching, real mesh +ownership or main's retained-request bridge. Those paths are source-inspected +only. Native current-revision evidence is still needed for target usability, +thin-outline contrast, warning-stack geometry, mixed camera gestures, both +window sizes/scales and accessibility. The topology probe is literal and does not +constitute actual imported-model interaction. No T4 handles/capture transaction, +authored runtime, archive custody, numerical acceptance or publication result is +implied. No production source or canonical plan was edited by this reviewer. diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index da689c15..83b4c4b8 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -56,6 +56,15 @@ impl Default for SceneUi { } } impl SceneUi { + #[cfg(test)] + pub(super) fn geometry_fixture(forces: ForceScene, model: bool) -> Self { + Self { + forces, + model_id: model.then_some(SceneObjectId(u64::MAX)), + ..Default::default() + } + } + pub fn draft(&self) -> &ForceScene { &self.forces } diff --git a/src/bin/desktop/scene_view.rs b/src/bin/desktop/scene_view.rs new file mode 100644 index 00000000..9d7f59aa --- /dev/null +++ b/src/bin/desktop/scene_view.rs @@ -0,0 +1,1050 @@ +//! Visual authoring geometry only: no field evaluation, solver or model loading. +use super::{ + camera::OrbitCamera, + scene_ui::{SceneContext, SceneUi}, +}; +use eframe::egui::{self, Color32, Pos2, Rect, Stroke}; +use rheon::{ForceKind, ForceObject, ForceSupport, SceneObjectId}; +use std::collections::BTreeMap; +const NEAR: f32 = 0.0151; +const PICK_RADIUS: f32 = 7.0; +const MODEL_TRIANGLE_LIMIT: usize = 12_000; +// Display-only coordinates are bounded to prevent projection/interpolation +// overflow. Refuse explicitly; never clamp or rewrite physical authoring values. +type V3 = [f32; 3]; +#[derive(Clone, Copy)] +pub struct MeshRef<'a> { + pub vertices: &'a [[f64; 3]], + pub triangles: &'a [[usize; 3]], +} +#[derive(Clone, Copy, Debug)] +struct Point { + pos: Pos2, + depth: f32, +} +#[derive(Clone, Copy, Debug)] +struct Line { + a: Point, + b: Point, +} +struct Object { + id: SceneObjectId, + name: String, + enabled: bool, + anchor: Option, + lines: Vec, +} +struct Model { + id: SceneObjectId, + triangles: Vec<[Point; 3]>, + outline: Vec, +} +struct Projection { + objects: Vec, + model: Option, + warning: Option<&'static str>, + pick_incomplete: bool, + orthographic: bool, +} +#[derive(Clone, Copy, Debug)] +struct Hit { + id: SceneObjectId, + distance: f32, + depth: f32, +} +fn dot(a: V3, b: V3) -> f32 { + a.into_iter().zip(b).map(|(a, b)| a * b).sum() +} +fn sub(a: V3, b: V3) -> V3 { + std::array::from_fn(|d| a[d] - b[d]) +} +fn cross(a: V3, b: V3) -> V3 { + [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ] +} +fn lerp(a: V3, b: V3, t: f32) -> V3 { + std::array::from_fn(|d| a[d] + (b[d] - a[d]) * t) +} +fn normalized(p: [f64; 3], side: f64) -> Option { + let p = p.map(|v| v / side); + (side.is_finite() && side > 0.0 && p.iter().all(|v| v.is_finite() && v.abs() <= 1.0e12)) + .then(|| p.map(|v| v as f32)) +} +fn project(camera: &OrbitCamera, p: V3, rect: Rect) -> Option { + let (pos, depth) = camera.project(p, rect)?; + (pos.is_finite() && depth.is_finite() && depth >= NEAR - 0.00001) + .then_some(Point { pos, depth }) +} +fn depth(camera: &OrbitCamera, p: V3) -> f32 { + let (eye, forward, _, _) = camera.basis(); + dot(sub(p, eye), forward) +} +fn interpolate_depth(a: f32, b: f32, t: f64, ortho: bool) -> f32 { + if ortho { + (f64::from(a) + (f64::from(b) - f64::from(a)) * t) as f32 + } else { + (1.0 / ((1.0 - t) / f64::from(a) + t / f64::from(b))) as f32 + } +} +/// Clip before painting as well as picking; finite but enormous offscreen points +/// must not reach the tessellator. Depth follows screen interpolation correctly. +fn clip_line(line: Line, rect: Rect, ortho: bool) -> Option { + let a = [f64::from(line.a.pos.x), f64::from(line.a.pos.y)]; + let b = [f64::from(line.b.pos.x), f64::from(line.b.pos.y)]; + if !a.into_iter().chain(b).all(f64::is_finite) { + return None; + } + let delta = [b[0] - a[0], b[1] - a[1]]; + let mut lower: f64 = 0.0; + let mut upper: f64 = 1.0; + for (p, q) in [ + (-delta[0], a[0] - f64::from(rect.left())), + (delta[0], f64::from(rect.right()) - a[0]), + (-delta[1], a[1] - f64::from(rect.top())), + (delta[1], f64::from(rect.bottom()) - a[1]), + ] { + if p == 0.0 { + if q < 0.0 { + return None; + } + } else { + let t = q / p; + if p < 0.0 { + lower = lower.max(t); + } else { + upper = upper.min(t); + } + if lower > upper { + return None; + } + } + } + let point = |t: f64| Point { + pos: egui::pos2((a[0] + delta[0] * t) as f32, (a[1] + delta[1] * t) as f32), + depth: interpolate_depth(line.a.depth, line.b.depth, t, ortho), + }; + Some(Line { + a: point(lower), + b: point(upper), + }) +} +fn segment(camera: &OrbitCamera, rect: Rect, mut a: V3, mut b: V3) -> Option { + let da = depth(camera, a); + let db = depth(camera, b); + if !da.is_finite() || !db.is_finite() || (da < NEAR && db < NEAR) { + return None; + } + if da < NEAR { + a = lerp(a, b, (NEAR - da) / (db - da)); + } else if db < NEAR { + b = lerp(a, b, (NEAR - da) / (db - da)); + } + clip_line( + Line { + a: project(camera, a, rect)?, + b: project(camera, b, rect)?, + }, + rect, + camera.orthographic, + ) +} +fn clipped_triangle(camera: &OrbitCamera, rect: Rect, triangle: [V3; 3]) -> Vec<[Point; 3]> { + let mut polygon = Vec::with_capacity(4); + for i in 0..3 { + let a = triangle[i]; + let b = triangle[(i + 1) % 3]; + let da = depth(camera, a); + let db = depth(camera, b); + if !da.is_finite() || !db.is_finite() { + return vec![]; + } + if da >= NEAR { + polygon.push(a); + } + if (da >= NEAR) != (db >= NEAR) { + polygon.push(lerp(a, b, (NEAR - da) / (db - da))); + } + } + let projected: Option> = polygon + .into_iter() + .map(|p| project(camera, p, rect)) + .collect(); + let Some(p) = projected else { + return vec![]; + }; + (1..p.len().saturating_sub(1)) + .map(|i| [p[0], p[i], p[i + 1]]) + .collect() +} +fn line_hit(line: Line, p: Pos2, ortho: bool) -> Option<(f32, f32)> { + let vector = line.b.pos - line.a.pos; + let length = vector.length_sq(); + let t = if length > 0.0001 { + ((p - line.a.pos).dot(vector) / length).clamp(0.0, 1.0) + } else { + 0.0 + }; + let distance = p.distance(line.a.pos + vector * t); + (distance <= PICK_RADIUS).then(|| { + ( + distance, + interpolate_depth(line.a.depth, line.b.depth, f64::from(t), ortho), + ) + }) +} +fn triangle_hit(triangle: [Point; 3], p: Pos2, ortho: bool) -> Option { + let [a, b, c] = triangle; + let cross = |u: egui::Vec2, v: egui::Vec2| { + f64::from(u.x) * f64::from(v.y) - f64::from(u.y) * f64::from(v.x) + }; + let area = cross(b.pos - a.pos, c.pos - a.pos); + if !area.is_finite() || area.abs() < 0.01 { + return None; + } + let u = cross(b.pos - p, c.pos - p) / area; + let v = cross(c.pos - p, a.pos - p) / area; + let w = 1.0 - u - v; + if [u, v, w].iter().any(|v| *v < -1e-6 || *v > 1.0 + 1e-6) { + return None; + } + let z = if ortho { + u * f64::from(a.depth) + v * f64::from(b.depth) + w * f64::from(c.depth) + } else { + 1.0 / (u / f64::from(a.depth) + v / f64::from(b.depth) + w / f64::from(c.depth)) + }; + (z.is_finite() && z > 0.0).then_some(z as f32) +} +fn rotate([x, y, z, w]: [f64; 4], [a, b, c]: [f64; 3]) -> [f64; 3] { + [ + (1.0 - 2.0 * (y * y + z * z)) * a + 2.0 * (x * y - z * w) * b + 2.0 * (x * z + y * w) * c, + 2.0 * (x * y + z * w) * a + (1.0 - 2.0 * (x * x + z * z)) * b + 2.0 * (y * z - x * w) * c, + 2.0 * (x * z - y * w) * a + 2.0 * (y * z + x * w) * b + (1.0 - 2.0 * (x * x + y * y)) * c, + ] +} +fn world(object: &ForceObject, local: [f64; 3]) -> [f64; 3] { + let p = rotate(object.transform.rotation, local); + std::array::from_fn(|d| p[d] + object.transform.translation[d]) +} +fn force_lines(object: &ForceObject, side: f64) -> Vec<([f64; 3], [f64; 3])> { + let mut lines = Vec::with_capacity(128); + let size = side * 0.055; + let mut push = |a, b| lines.push((world(object, a), world(object, b))); + match object.support { + ForceSupport::Global => {} + ForceSupport::Sphere { radius, .. } => { + for axis in 0..3 { + for i in 0..32 { + let p = |i: usize| { + let angle = i as f64 * std::f64::consts::TAU / 32.0; + let mut p = [0.0; 3]; + p[(axis + 1) % 3] = radius * angle.cos(); + p[(axis + 2) % 3] = radius * angle.sin(); + p + }; + push(p(i), p(i + 1)); + } + } + } + ForceSupport::Box { half_extent, .. } => { + for axis in 0..3 { + for a in [-1.0, 1.0] { + for b in [-1.0, 1.0] { + let mut p = half_extent; + p[axis] = -half_extent[axis]; + p[(axis + 1) % 3] *= a; + p[(axis + 2) % 3] *= b; + let mut q = p; + q[axis] = half_extent[axis]; + push(p, q); + } + } + } + } + } + // A fixed authoring glyph, deliberately unrelated to force magnitude/density. + match object.kind { + ForceKind::Directional { vector } => { + let max = vector.iter().fold(0.0_f64, |a, v| a.max(v.abs())); + if max > 0.0 { + let v = vector.map(|v| v / max); + let norm = v.iter().map(|v| v * v).sum::().sqrt(); + let end = v.map(|v| v / norm * size * 2.0); + push([0.0; 3], end); + let axis = if v[1].abs() < 0.9 { + [0.0, 1.0, 0.0] + } else { + [1.0, 0.0, 0.0] + }; + let tangent = [ + v[1] * axis[2] - v[2] * axis[1], + v[2] * axis[0] - v[0] * axis[2], + v[0] * axis[1] - v[1] * axis[0], + ]; + for sign in [-1.0, 1.0] { + push( + end, + std::array::from_fn(|d| end[d] * 0.75 + tangent[d] * size * 0.25 * sign), + ); + } + } + } + ForceKind::Radial { strength } if strength != 0.0 => { + for d in 0..3 { + for sign in [-1.0, 1.0] { + let mut end = [0.0; 3]; + end[d] = size * sign; + let (start, tip) = if strength > 0.0 { + ([0.0; 3], end) + } else { + (end, [0.0; 3]) + }; + push(start, tip); + for wing in [-1.0, 1.0] { + let mut tail = tip; + tail[d] += (start[d] - tip[d]) * 0.25; + tail[(d + 1) % 3] = size * 0.16 * wing; + push(tip, tail); + } + } + } + } + ForceKind::Vortex { strength } if strength != 0.0 => { + let sign = if strength < 0.0 { -1.0 } else { 1.0 }; + let p = |i: usize| { + let a = i as f64 * std::f64::consts::TAU / 24.0; + [size * a.cos(), 0.0, -size * a.sin() * sign] + }; + for i in 0..24 { + push(p(i), p(i + 1)); + } + push(p(0), [size * 0.7, 0.0, size * 0.3 * sign]); + } + _ => {} + } + lines +} +impl Projection { + fn build( + state: &SceneUi, + camera: &OrbitCamera, + rect: Rect, + side: f64, + mesh: Option>, + ) -> Self { + let mut result = Self { + objects: vec![], + model: None, + warning: None, + pick_incomplete: false, + orthographic: camera.orthographic, + }; + let (eye, forward, right, up) = camera.basis(); + if !rect.is_finite() + || rect.width() <= 0.0 + || rect.height() <= 0.0 + || rect.width() > 1e6 + || rect.height() > 1e6 + || !camera.distance.is_finite() + || camera.distance < 1e-6 + || !eye + .into_iter() + .chain(forward) + .chain(right) + .chain(up) + .all(f32::is_finite) + || [forward, right, up] + .iter() + .any(|v| (dot(*v, *v) - 1.0).abs() > 0.01) + { + result.warning = Some("Scene selection unavailable · invalid camera or canvas"); + result.pick_incomplete = true; + return result; + } + if state.context == SceneContext::Draft { + if state.draft().freeze().is_err() { + result.warning = Some("Invalid force draft · fix properties to preview"); + result.pick_incomplete = true; + } else { + for object in &state.draft().objects { + let anchor_position = normalized(object.transform.translation, side); + let mut unavailable = anchor_position.is_none(); + let anchor = anchor_position.and_then(|p| project(camera, p, rect)); + let lines = force_lines(object, side) + .into_iter() + .filter_map(|(a, b)| match (normalized(a, side), normalized(b, side)) { + (Some(a), Some(b)) => segment(camera, rect, a, b), + _ => { + unavailable = true; + None + } + }) + .collect(); + if unavailable { + result.pick_incomplete = true; + result.warning = + Some("Force preview out of display range · use properties"); + } + result.objects.push(Object { + id: object.id, + name: object.name.clone(), + enabled: object.enabled, + anchor, + lines, + }); + } + } + } + if let (Some(id), Some(mesh)) = (state.model_id(), mesh) { + if mesh.triangles.len() > MODEL_TRIANGLE_LIMIT { + result.warning = Some("Model selection unavailable above 12,000 triangles"); + result.pick_incomplete = true; + return result; + } + let mut model = Model { + id, + triangles: vec![], + outline: vec![], + }; + // Weld only exactly equal positions (including signed zero), never nearby + // physical vertices. Opposite-vertex screen side is winding independent. + type Key = [u64; 3]; + let key = |p: [f64; 3]| p.map(|v| if v == 0.0 { 0 } else { v.to_bits() }); + let mut edges: BTreeMap<(Key, Key), (u8, usize, V3, V3)> = BTreeMap::new(); + let (eye, forward, _, _) = camera.basis(); + let mut bad = false; + for triangle in mesh.triangles { + let raw: Option> = triangle + .iter() + .map(|&i| mesh.vertices.get(i).copied()) + .collect(); + let Some(raw) = raw else { + bad = true; + continue; + }; + let points: Option> = raw.iter().map(|&p| normalized(p, side)).collect(); + let Some(p) = points else { + bad = true; + continue; + }; + let normal = cross(sub(p[1], p[0]), sub(p[2], p[0])); + let area = dot(normal, normal); + if !area.is_finite() { + bad = true; + continue; + } + if area == 0.0 { + continue; + } + model + .triangles + .extend(clipped_triangle(camera, rect, [p[0], p[1], p[2]])); + for i in 0..3 { + let j = (i + 1) % 3; + let k = (i + 2) % 3; + let (ka, kb, a, b) = if key(raw[i]) < key(raw[j]) { + (key(raw[i]), key(raw[j]), p[i], p[j]) + } else { + (key(raw[j]), key(raw[i]), p[j], p[i]) + }; + let view = if camera.orthographic { + forward.map(|v| -v) + } else { + sub(eye, a) + }; + let side = dot(cross(sub(b, a), sub(p[k], a)), view); + let flag = if side > 0.0 { + 1 + } else if side < 0.0 { + 2 + } else { + 0 + }; + let edge = edges.entry((ka, kb)).or_insert((0, 0, a, b)); + edge.0 |= flag; + edge.1 += 1; + } + } + let nonmanifold = edges.values().any(|e| e.1 > 2); + if bad { + result.pick_incomplete = true; + result.warning = Some("Model selection incomplete · invalid display geometry"); + } + if nonmanifold { + result.warning = Some("Model outline unavailable · non-manifold edges"); + } else { + for (_, (sides, count, a, b)) in edges { + if (count == 1 || sides != 3) + && let Some(line) = segment(camera, rect, a, b) + { + model.outline.push(line); + } + } + } + result.model = Some(model); + } + if state.model_id().is_some() && mesh.is_none() { + result.warning = Some("Model selection unavailable · geometry absent"); + result.pick_incomplete = true; + } + result + } + fn pick(&self, p: Pos2, rect: Rect) -> Result, ()> { + if !p.is_finite() || !rect.contains(p) { + return Err(()); + } + // Glyphs/support wires are x-ray overlays; their stroke hit takes priority + // over underlying model surfaces, matching the visible rendering order. + let mut hits = vec![]; + for object in &self.objects { + if let Some(anchor) = object.anchor.filter(|p| rect.contains(p.pos)) { + let distance = anchor.pos.distance(p); + if distance <= PICK_RADIUS { + hits.push(Hit { + id: object.id, + distance, + depth: anchor.depth, + }); + } + } + for &line in &object.lines { + if let Some((distance, depth)) = line_hit(line, p, self.orthographic) { + hits.push(Hit { + id: object.id, + distance, + depth, + }); + } + } + } + hits.sort_by(|a, b| { + a.distance + .total_cmp(&b.distance) + .then(a.depth.total_cmp(&b.depth)) + .then(a.id.0.cmp(&b.id.0)) + }); + if let Some(hit) = hits.first() { + return Ok(Some(hit.id)); + } + let hit = self.model.as_ref().and_then(|model| { + model + .triangles + .iter() + .filter_map(|&t| triangle_hit(t, p, self.orthographic)) + .min_by(f32::total_cmp) + .map(|_| model.id) + }); + if hit.is_none() && self.pick_incomplete { + Err(()) + } else { + Ok(hit) + } + } +} +pub fn interact( + ui: &egui::Ui, + response: &egui::Response, + camera: &OrbitCamera, + side: f64, + mesh: Option>, + state: &mut SceneUi, +) { + if response.clicked_by(egui::PointerButton::Primary) + && ui.input(|i| i.modifiers.is_none()) + && let Some(p) = response.interact_pointer_pos() + { + let projection = Projection::build(state, camera, response.rect, side, mesh); + if let Ok(hit) = projection.pick(p, response.rect) { + state.select(hit); + if hit.is_some() { + state.open = true; + } + } + response.request_focus(); + } +} +pub struct PaintArea { + pub rect: Rect, + pub warning_top: f32, +} +impl From for PaintArea { + fn from(rect: Rect) -> Self { + Self { + rect, + warning_top: rect.top() + 12.0, + } + } +} +pub fn paint( + ui: &egui::Ui, + area: PaintArea, + camera: &OrbitCamera, + side: f64, + mesh: Option>, + state: &SceneUi, +) { + let rect = area.rect; + let projection = Projection::build(state, camera, rect, side, mesh); + ui.interact(rect,ui.id().with("scene-marker-help"),egui::Sense::hover()).on_hover_text("Click a force marker or model to select. Force markers are x-ray authoring aids; glyph size does not represent magnitude. Zero strengths use a neutral anchor. Support wires use metres. Edit properties in Scene."); + let painter = ui.painter_at(rect); + let selected = state.selected(); + if let Some(warning) = projection.warning { + let galley = painter.layout( + warning.into(), + egui::FontId::proportional(12.0), + Color32::YELLOW, + (rect.width() - 24.0).max(1.0), + ); + painter.galley( + egui::pos2(rect.left() + 12.0, area.warning_top), + galley, + Color32::YELLOW, + ); + } + for object in &projection.objects { + let is_selected = selected == Some(object.id); + let color = if is_selected { + Color32::from_rgb(230, 245, 255) + } else if object.enabled { + Color32::from_rgb(90, 180, 215) + } else { + Color32::from_rgb(115, 125, 135) + }; + for line in &object.lines { + painter.line_segment( + [line.a.pos, line.b.pos], + Stroke::new(if is_selected { 1.35 } else { 0.8 }, color), + ); + } + if let Some(anchor) = object.anchor.filter(|p| rect.contains(p.pos)) { + painter.circle_stroke( + anchor.pos, + if is_selected { 4.0 } else { 3.0 }, + Stroke::new(1.0, color), + ); + if is_selected { + let mut job = egui::text::LayoutJob::simple( + object.name.clone(), + egui::FontId::proportional(12.0), + color, + (rect.width() - 20.0).clamp(1.0, 180.0), + ); + job.wrap.max_rows = 1; + job.wrap.break_anywhere = true; + let galley = painter.layout_job(job); + let origin = egui::pos2( + (anchor.pos.x + 10.0).clamp( + rect.left(), + (rect.right() - galley.size().x).max(rect.left()), + ), + (anchor.pos.y - 22.0).clamp( + rect.top(), + (rect.bottom() - galley.size().y).max(rect.top()), + ), + ); + painter.galley(origin, galley, color); + } + } + } + if let Some(model) = &projection.model + && selected == Some(model.id) + { + for line in &model.outline { + painter.line_segment( + [line.a.pos, line.b.pos], + Stroke::new(1.35, Color32::from_rgb(230, 245, 255)), + ); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rheon::{ForceScene, ForceTime, ForceTransform, ForceUnits}; + fn force(id: u64, kind: ForceKind) -> ForceObject { + ForceObject { + id: SceneObjectId(id), + name: format!("Force {id}"), + enabled: true, + transform: ForceTransform { + translation: [0.5; 3], + ..Default::default() + }, + units: ForceUnits::Acceleration, + kind, + support: ForceSupport::Global, + time: ForceTime::default(), + } + } + fn camera() -> OrbitCamera { + OrbitCamera { + yaw: 0.0, + pitch: 0.0, + orthographic: true, + ..Default::default() + } + } + fn rect() -> Rect { + Rect::from_min_size(egui::pos2(20.0, 40.0), egui::vec2(600.0, 400.0)) + } + fn state(objects: Vec, model: bool) -> SceneUi { + SceneUi::geometry_fixture( + ForceScene { + version: 1, + objects, + }, + model, + ) + } + #[test] + fn geometry_depth_near_clip_and_degenerate_hits() { + let r = rect(); + let p = |x, y, z| Point { + pos: egui::pos2(x, y), + depth: z, + }; + let t = [p(0.0, 0.0, 1.0), p(100.0, 0.0, 2.0), p(0.0, 100.0, 4.0)]; + assert!((triangle_hit(t, egui::pos2(25.0, 25.0), true).unwrap() - 2.0).abs() < 1e-6); + assert!( + (triangle_hit(t, egui::pos2(25.0, 25.0), false).unwrap() - 1.0 / 0.6875).abs() < 1e-6 + ); + assert!(triangle_hit([t[0]; 3], egui::Pos2::ZERO, false).is_none()); + assert!(triangle_hit(t, egui::pos2(90.0, 90.0), false).is_none()); + let line = Line { + a: p(-1000.0, 100.0, 1.0), + b: p(1000.0, 100.0, 4.0), + }; + let clipped = clip_line(line, r, false).unwrap(); + assert_eq!(clipped.a.pos.x, r.left()); + assert_eq!(clipped.b.pos.x, r.right()); + assert!(clipped.a.depth > 1.0 && clipped.b.depth < 4.0); + assert!(line_hit(Line { a: t[0], b: t[0] }, egui::Pos2::ZERO, false).is_some()); + let mut c = camera(); + c.orthographic = false; + let (eye, forward, right, _) = c.basis(); + let at = |z: f32, x: f32| std::array::from_fn(|d| eye[d] + forward[d] * z + right[d] * x); + let near = segment(&c, r, at(-1.0, 0.0), at(1.0, 0.0)).unwrap(); + assert!(near.a.depth >= NEAR - 1e-5); + assert!(segment(&c, r, at(-2.0, 0.0), at(-1.0, 0.0)).is_none()); + assert!(!clipped_triangle(&c, r, [at(-1.0, 0.0), at(1.0, 0.1), at(1.0, -0.1)]).is_empty()); + assert!(normalized([f64::MAX; 3], 1.0).is_none()); + assert!(normalized([0.0; 3], 0.0).is_none()); + } + #[test] + fn geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold() { + let s = state(vec![], true); + let c = camera(); + let r = rect(); + let v = [ + [0.2, 0.2, 0.5], + [0.8, 0.2, 0.5], + [0.8, 0.8, 0.5], + [0.2, 0.8, 0.5], + ]; + // Two-sided input may reverse either triangle. The shared diagonal is not an outline. + for triangles in [[[0, 1, 2], [0, 2, 3]], [[2, 1, 0], [0, 2, 3]]] { + let projection = Projection::build( + &s, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &v, + triangles: &triangles, + }), + ); + assert_eq!(projection.model.as_ref().unwrap().outline.len(), 4); + assert!(projection.warning.is_none()); + assert_eq!(projection.pick(r.center(), r), Ok(s.model_id())); + } + let duplicate = [v[0], v[1], v[2], v[0], v[2], v[3]]; + let t = [[0, 1, 2], [5, 4, 3]]; + assert_eq!( + Projection::build( + &s, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &duplicate, + triangles: &t + }) + ) + .model + .unwrap() + .outline + .len(), + 4 + ); + let disconnected = [[0, 1, 2], [3, 4, 5]]; + let dv = [ + v[0], + v[1], + v[2], + [0.1, 0.1, 0.2], + [0.15, 0.1, 0.2], + [0.1, 0.15, 0.2], + ]; + assert_eq!( + Projection::build( + &s, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &dv, + triangles: &disconnected + }) + ) + .model + .unwrap() + .outline + .len(), + 6 + ); + let t = [[0, 1, 2], [0, 1, 3], [1, 0, 2]]; + let projection = Projection::build( + &s, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &v, + triangles: &t, + }), + ); + assert!(projection.warning.unwrap().contains("non-manifold")); + assert!(projection.model.as_ref().unwrap().outline.is_empty()); + assert_eq!(projection.pick(r.left_top(), r), Ok(None)); + } + #[test] + fn geometry_force_glyphs_preserve_zero_direction_sign_and_support() { + for kind in [ + ForceKind::Directional { vector: [0.0; 3] }, + ForceKind::Radial { strength: 0.0 }, + ForceKind::Vortex { strength: 0.0 }, + ] { + assert!(force_lines(&force(1, kind), 1.0).is_empty()); + } + let mut f = force( + 1, + ForceKind::Directional { + vector: [1e300, 0.0, 0.0], + }, + ); + let a = std::f64::consts::FRAC_PI_4; + f.transform.rotation = [0.0, 0.0, a.sin(), a.cos()]; + let line = force_lines(&f, 1.0)[0]; + assert!((line.1[0] - 0.5).abs() < 1e-12); + assert!((line.1[1] - 0.61).abs() < 1e-12); + for sign in [-1.0, 1.0] { + let lines = force_lines(&force(1, ForceKind::Radial { strength: sign }), 1.0); + if sign > 0.0 { + assert_eq!(lines[0].0, [0.5; 3]); + } else { + assert_eq!(lines[0].1, [0.5; 3]); + } + let lines = force_lines(&force(1, ForceKind::Vortex { strength: sign }), 1.0); + assert!((lines[0].1[2] - 0.5) * sign < 0.0); + } + f.kind = ForceKind::Radial { strength: 0.0 }; + f.transform = ForceTransform::default(); + f.support = ForceSupport::Box { + half_extent: [0.2, 0.3, 0.4], + edge_fraction: 0.0, + }; + let lines = force_lines(&f, 20.0); + assert_eq!(lines.len(), 12); + assert_eq!(lines[0].0, [-0.2, -0.3, -0.4]); + assert_eq!(lines[0].1, [0.2, -0.3, -0.4]); + } + #[test] + fn scene_pick_identity_overlap_retained_and_unavailable_preview() { + let a = force(2, ForceKind::Directional { vector: [0.0; 3] }); + let mut s = state(vec![a.clone(), force(1, a.kind)], false); + let c = camera(); + let r = rect(); + let projection = Projection::build(&s, &c, r, 1.0, None); + let hit = projection.pick(r.center(), r).unwrap(); + assert_eq!(hit, Some(SceneObjectId(1))); + s.select(hit); + assert_eq!(s.selected(), hit); + assert_eq!(projection.pick(r.left_top(), r), Ok(None)); + let mut disabled = a.clone(); + disabled.enabled = false; + let p = Projection::build(&state(vec![disabled], false), &c, r, 1.0, None); + assert!(!p.objects[0].enabled); + assert_eq!(p.pick(r.center(), r), Ok(Some(SceneObjectId(2)))); + let mut near = force(7, a.kind); + near.transform.translation[2] = 0.8; + let p = Projection::build(&state(vec![a.clone(), near], false), &c, r, 1.0, None); + assert_eq!(p.pick(r.center(), r), Ok(Some(SceneObjectId(7)))); + let m = state(vec![], true); + let vertices = [[0.2, 0.2, 0.5], [0.8, 0.2, 0.5], [0.2, 0.8, 0.5]]; + let triangles = [[0, 1, 2]]; + let p = Projection::build( + &m, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &vertices, + triangles: &triangles, + }), + ); + let outside = c.project([0.7, 0.7, 0.5], r).unwrap().0; + assert_eq!(p.pick(outside, r), Ok(None)); + let invisible = Projection { + objects: vec![Object { + id: SceneObjectId(9), + name: "Offscreen".into(), + enabled: true, + anchor: Some(Point { + pos: egui::pos2(r.left() - 1.0, 100.0), + depth: 1.0, + }), + lines: vec![], + }], + model: None, + warning: None, + pick_incomplete: false, + orthographic: true, + }; + assert_eq!( + invisible.pick(egui::pos2(r.left() + 1.0, 100.0), r), + Ok(None) + ); + let mut invalid_camera = c.clone(); + invalid_camera.yaw = f32::NAN; + let p = Projection::build(&s, &invalid_camera, r, 1.0, None); + assert!(p.pick_incomplete); + assert!(p.pick(r.center(), r).is_err()); + let mut invalid = a.clone(); + invalid.support = ForceSupport::Sphere { + radius: -1.0, + edge_fraction: 0.0, + }; + let mut invalid = state(vec![invalid], false); + invalid.select(Some(SceneObjectId(2))); + let p = Projection::build(&invalid, &c, r, 1.0, None); + assert!(p.warning.is_some()); + assert!(p.pick(r.center(), r).is_err()); + assert_eq!(invalid.selected(), Some(SceneObjectId(2))); + let mut large = a.clone(); + large.transform.translation = [1e37, 0.5, 0.5]; + let p = Projection::build(&state(vec![large], false), &c, r, 1.0, None); + assert!(p.pick_incomplete); + assert!(p.warning.is_some()); + let mut far = a.clone(); + far.transform.translation = [f64::MAX; 3]; + let p = Projection::build(&state(vec![far], false), &c, r, 1.0, None); + assert!(p.warning.is_some()); + assert!(p.pick(r.center(), r).is_err()); + s.begin_run(false); + let p = Projection::build(&s, &c, r, 1.0, None); + assert!(p.objects.is_empty()); + assert_eq!(p.pick(r.center(), r), Ok(None)); + assert_eq!(s.selected(), None); + let model = state(vec![], true); + let t = vec![[0, 1, 2]; MODEL_TRIANGLE_LIMIT + 1]; + let p = Projection::build( + &model, + &c, + r, + 1.0, + Some(MeshRef { + vertices: &[], + triangles: &t, + }), + ); + assert!(p.warning.unwrap().contains("12,000")); + assert!(p.pick(r.center(), r).is_err()); + } + #[test] + fn scene_pointer_selects_same_identity_ignores_drag_and_modified_click() { + let ctx = egui::Context::default(); + let mut s = state(vec![force(1, ForceKind::Radial { strength: 0.0 })], false); + let c = camera(); + let frame = |events: Vec, modifiers: egui::Modifiers, s: &mut SceneUi| { + let mut response_rect = Rect::NOTHING; + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(700.0, 500.0), + )), + events, + modifiers, + ..Default::default() + }, + |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let (r, response) = ui.allocate_exact_size( + ui.available_size(), + egui::Sense::click_and_drag(), + ); + response_rect = r; + interact(ui, &response, &c, 1.0, None, s); + paint(ui, r.into(), &c, 1.0, None, s); + }); + }, + ); + response_rect + }; + let r = frame(vec![], egui::Modifiers::NONE, &mut s); + let p = r.center(); + let button = |pos, pressed, modifiers| egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers, + }; + for modifiers in [egui::Modifiers::SHIFT, egui::Modifiers::NONE] { + frame( + vec![egui::Event::PointerMoved(p), button(p, true, modifiers)], + modifiers, + &mut s, + ); + frame(vec![button(p, false, modifiers)], modifiers, &mut s); + assert_eq!( + s.selected(), + if modifiers.is_none() { + Some(SceneObjectId(1)) + } else { + None + } + ); + } + let end = r.left_top() + egui::vec2(30.0, 30.0); + frame( + vec![button(p, true, egui::Modifiers::NONE)], + egui::Modifiers::NONE, + &mut s, + ); + frame( + vec![egui::Event::PointerMoved(end)], + egui::Modifiers::NONE, + &mut s, + ); + frame( + vec![button(end, false, egui::Modifiers::NONE)], + egui::Modifiers::NONE, + &mut s, + ); + assert_eq!(s.selected(), Some(SceneObjectId(1))); + frame( + vec![button(end, true, egui::Modifiers::NONE)], + egui::Modifiers::NONE, + &mut s, + ); + frame( + vec![button(end, false, egui::Modifiers::NONE)], + egui::Modifiers::NONE, + &mut s, + ); + assert_eq!(s.selected(), None); + } +} diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index 386894cb..4527ea6c 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -96,7 +96,16 @@ impl Default for Viewport { } } } +pub struct ViewInputs<'a> { + pub frame: Option>, + pub emitter: Option, + pub workers: &'a [WorkerVoxel], + pub grid_edge: usize, + pub inspector: Option<&'a super::inspector::Inspector>, + pub scene: Option<&'a mut super::scene_ui::SceneUi>, +} impl Viewport { + #[cfg(test)] pub fn show( &mut self, ui: &mut egui::Ui, @@ -106,6 +115,27 @@ impl Viewport { grid_edge: usize, inspector: Option<&super::inspector::Inspector>, ) { + self.show_scene( + ui, + ViewInputs { + frame, + emitter, + workers, + grid_edge, + inspector, + scene: None, + }, + ); + } + pub fn show_scene(&mut self, ui: &mut egui::Ui, inputs: ViewInputs<'_>) { + let ViewInputs { + frame, + emitter, + workers, + grid_edge, + inspector, + mut scene, + } = inputs; if self.authored_forces_pending { self.mode = ViewMode::Appearance; } @@ -181,6 +211,16 @@ impl Viewport { return; } self.input(ui, &response, rect, 2.35); + if let Some(scene) = scene.as_deref_mut() { + let mesh = self + .collision_mesh + .as_ref() + .map(|m| super::scene_view::MeshRef { + vertices: m.vertices(), + triangles: m.triangles(), + }); + super::scene_view::interact(ui, &response, &self.camera, self.domain_side, mesh, scene); + } let changed = match (&self.frame, &frame) { (Some(a), Some(b)) => !Arc::ptr_eq(a, b), (None, None) => false, @@ -341,6 +381,7 @@ impl Viewport { caption_color, (rect.width() - 28.0).max(1.0), ); + let mut scene_warning_top = rect.top() + 12.0 + caption.size().y + 8.0; if let Some(mesh) = &self.collision_mesh { const DISPLAY_TRIANGLES: usize = 12_000; let stride = mesh.triangles().len().div_ceil(DISPLAY_TRIANGLES).max(1); @@ -369,6 +410,7 @@ impl Viewport { mesh_color, (rect.width() - 28.0).max(1.0), ); + scene_warning_top += mesh_caption.size().y + 8.0; painter.galley( rect.left_top() + egui::vec2(14.0, 12.0 + caption.size().y + 8.0), mesh_caption, @@ -451,6 +493,26 @@ impl Viewport { ); let help_origin = rect.left_bottom() + egui::vec2(14.0, -12.0 - help.size().y); painter.galley(help_origin, help, help_color); + if let Some(scene) = scene { + let mesh = self + .collision_mesh + .as_ref() + .map(|m| super::scene_view::MeshRef { + vertices: m.vertices(), + triangles: m.triangles(), + }); + super::scene_view::paint( + ui, + super::scene_view::PaintArea { + rect, + warning_top: scene_warning_top, + }, + &self.camera, + self.domain_side, + mesh, + scene, + ); + } } fn input(&mut self, ui: &egui::Ui, response: &egui::Response, rect: Rect, frame_distance: f32) { if !(response.hovered() || response.dragged()) { diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index ef66c74b..a5ca85df 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -54,6 +54,8 @@ use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; #[path = "desktop/scene_ui.rs"] mod scene_ui; +#[path = "desktop/scene_view.rs"] +mod scene_view; #[path = "desktop/settings.rs"] mod settings; #[cfg(test)] @@ -2509,16 +2511,20 @@ impl eframe::App for App { egui::CentralPanel::default() .frame(egui::Frame::NONE) .show(ctx, |ui| { - self.viewport.show( + self.viewport.show_scene( ui, - frame, - emitter, - &workers, - grid, - self.history - .as_ref() - .filter(|_| retained) - .map(|h| &h.inspector), + viewport::ViewInputs { + frame, + emitter, + workers: &workers, + grid_edge: grid, + inspector: self + .history + .as_ref() + .filter(|_| retained) + .map(|h| &h.inspector), + scene: Some(&mut self.scene), + }, ) }); if self.worker.is_some() || self.playing { From 8504b3e7c9e6a02a6e2521b54c1aefc9c0f58ec2 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 23:30:46 +0000 Subject: [PATCH 11/25] Keep viewport tab labels whole in narrow headers --- .../responsive-header-20261020/.gitattributes | 1 + .../responsive-header-20261020/manifest.json | 33 ++++ .../rheon-ui-responsive-isolated-check.log | 3 + .../rheon-ui-responsive-isolated-clippy.log | 2 + .../rheon-ui-responsive-isolated-diff.log | 0 .../rheon-ui-responsive-isolated-exact.log | 8 + .../rheon-ui-responsive-isolated-fmt.log | 0 .../rheon-ui-responsive-isolated-list2.log | 187 ++++++++++++++++++ docs/plans/ui-refinement/execution-ledger.md | 30 +++ .../reports/responsive-header-checkpoint.md | 22 +++ .../responsive-header-source-review.md | 91 +++++++++ src/bin/desktop/viewport.rs | 83 +++++++- 12 files changed, 450 insertions(+), 10 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-check.log create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-diff.log create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-exact.log create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-list2.log create mode 100644 docs/plans/ui-refinement/reports/responsive-header-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/responsive-header-source-review.md diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/.gitattributes b/docs/plans/ui-refinement/evidence/responsive-header-20261020/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/manifest.json b/docs/plans/ui-refinement/evidence/responsive-header-20261020/manifest.json new file mode 100644 index 00000000..5d317268 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/manifest.json @@ -0,0 +1,33 @@ +{ + "base": "60f3498f19403fc449aa2d868a4fdf4238fa786e", + "repair_source_sha256": "abbaf658447dcc946fa8c51fe48d6c9597af8f80da4592b220152820042f1eb8", + "patch_sha256": "901622bd12cbb09fc5729547ba3c108c85f0dd6abcfc026b9d6bc9139a998a44", + "exact_test": "viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent", + "isolation": "/workspace/Rheon-ui-header-verify contains only header repair, no T4 code", + "logs": { + "rheon-ui-responsive-isolated-list2.log": { + "sha256": "a884852338452bf14a788c0d8affff9a40f7d258d328846fd807e7d5e5eeae23", + "bytes": 19335 + }, + "rheon-ui-responsive-isolated-exact.log": { + "sha256": "5b3d85052bb8b38f8eeab61d001aba984326c843dd862d1d379de14092697f16", + "bytes": 411 + }, + "rheon-ui-responsive-isolated-clippy.log": { + "sha256": "07573c823e169c26a05658e8988298906e7fe25483512e36d69eb751362e768d", + "bytes": 135 + }, + "rheon-ui-responsive-isolated-check.log": { + "sha256": "bc06d5dfbcf5e2fe7768f0fbfa68a7deeb88fc433cbcfb030c39e784d1cabba1", + "bytes": 186 + }, + "rheon-ui-responsive-isolated-fmt.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0 + }, + "rheon-ui-responsive-isolated-diff.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0 + } + } +} diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-check.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-check.log new file mode 100644 index 00000000..daded0d7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-check.log @@ -0,0 +1,3 @@ + Blocking waiting for file lock on package cache + Checking rheon v0.1.0 (/workspace/Rheon-ui-header-verify) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.12s diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-clippy.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-clippy.log new file mode 100644 index 00000000..5a9321fa --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-header-verify) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 10.95s diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-diff.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-diff.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-exact.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-exact.log new file mode 100644 index 00000000..45c73938 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-exact.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 182 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-fmt.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-list2.log b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-list2.log new file mode 100644 index 00000000..cb6cb004 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/responsive-header-20261020/rheon-ui-responsive-isolated-list2.log @@ -0,0 +1,187 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +183 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 11ec7a2b..b6171d0f 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -359,3 +359,33 @@ Independent plan/source reports record all corrections. No DISPLAY/WAYLAND_DISPL new exact revision requires native render/pick/focus/high-DPI evidence separately. Runtime/archive force readiness still refuses; T4 handle plan reviewed, not accepted as implemented. Original M2a/precision and incident evidence remain unchanged. + +##6127 native receipt and separate responsive tab repair —2026-10-20 + +Parent validator reports exact6127cec/tree4a779f idle authoring passed: force +editing/validation, selection clearing, accepted model replacement, precision text +retention, Scene hide/reopen, run refusal including a sole disabled zero force. +Both sessions closed; no workloads. JPEGs were actually retained by that validator; +this owner has no Library IDs or local display. First-session stdout was overwritten +during native capture, and capture labels were corrected/documented: do not claim +complete native log retention. Retained/timeline playback still lacks an App +presentation fixture; an archive viewer cannot serve as that scene reconstruction. + +Observed900×650/150%, Scene hidden: Transport session label wrapped vertically, +header consumed about322px (validator report). Repair uses existing named selectable +buttons with Extend text wrap, preserving whole labels and outer whole-control wrap, +disabled reason, keyboard/accessibility and change response semantics. Independently +reviewed. Header-only staged source SHAabbaf658447dcc946fa8c51fe48d6c9597af8f80da4592b220152820042f1eb8. +Isolated60f3498 worktree with only this repair: list183, individually exact inert +header test1passed (100/150%, enabled/disabled), strictClippy/check/fmt/diff passed. +Raw evidence: evidence/responsive-header-20261020/manifest.json. No T4 source is in +that build/repair commit. Native actual repaired-header verification still pending; +repeat the reported exact state plus enabled/disabled named-tab keyboard behavior, +record actual quality85JPEG with revision/state. No numerical lab selection/run is +needed: Appearance idle is sufficient to see all tab labels and wrapping. + +A separately reviewed test-only workerless synthetic presentation harness is now +authorized for retained/timeline behavior. It must contain a legal frozen Request, +two ordered literal preview frames with distinct timestamps and two tracks, label +synthetic data, and remain absent from production launch. No implementation or +acceptance for that harness is claimed in this header checkpoint. diff --git a/docs/plans/ui-refinement/reports/responsive-header-checkpoint.md b/docs/plans/ui-refinement/reports/responsive-header-checkpoint.md new file mode 100644 index 00000000..450a0a42 --- /dev/null +++ b/docs/plans/ui-refinement/reports/responsive-header-checkpoint.md @@ -0,0 +1,22 @@ +# Responsive header repair + +Parent:60f3498f19403fc449aa2d868a4fdf4238fa786e. Minimal repair to native-observed +900×650/150% Scene-hidden tall Transport session tab: full named selectable buttons +use Extend text wrapping, while the existing horizontal-wrapped header moves whole +controls between rows. Preserve disabled reason/selection/keyboard/accessibility, +and selectable_value's mark_changed behavior. No shorter unexplained icons/fonts. + +Header-only source SHAabbaf658447dcc946fa8c51fe48d6c9597af8f80da4592b220152820042f1eb8; +patch SHA901622bd12cbb09fc5729547ba3c108c85f0dd6abcfc026b9d6bc9139a998a44. +Isolated source contains no unfinished T4 code. One exact header test passes after +183-name list, strict production Clippy/desktop check/fmt/diff pass; independent +review reports/responsive-header-source-review.md. Evidence manifest +SHA dfbe770f6d49ad576b1c73910472530bdda4696e17741ddc66da9f7702757ee8. + +Native handoff: verify this distinct published checkpoint/source, repeat Appearance +idle at900×650100/150%, Scene hidden and open, setup drawer visible/hidden, enabled +and authored-disabled tabs. Inspect full labels/whole-control wrapping and remaining +viewport height, keyboard focus and disabled reason. Do not select numerical labs +or launch. Capture real quality85JPEGs labelled revision/scale/state. No repaired +native or full header usability evidence is claimed here. Preserve6127 validation +seals and first-session stdout-overwrite limitation. diff --git a/docs/plans/ui-refinement/reports/responsive-header-source-review.md b/docs/plans/ui-refinement/reports/responsive-header-source-review.md new file mode 100644 index 00000000..5cb7badd --- /dev/null +++ b/docs/plans/ui-refinement/reports/responsive-header-source-review.md @@ -0,0 +1,91 @@ +# Independent responsive viewport header review + +2026-10-20. Reviewed only the responsive mode-tab repair relative to +`60f3498f19403fc449aa2d868a4fdf4238fa786e` in +`/workspace/Rheon-ui-authoring`. The parent relayed a native 900×650/150% case +where the Transport session label wrapped internally and consumed excessive +height. This reviewer did not perform that native observation. In-progress T4 +changes in the same working file are outside this review and must remain separate. +Only this report was authored; no tests, builds, native UI or workloads were run. + +The small `view_mode_tabs` helper retains all four full labels and selectable +button affordances. `TextWrapMode::Extend` prevents word wrapping within one +button, while the existing outer `horizontal_wrapped` layout moves whole buttons +between rows. It preserves the authored-force disabled policy and explanatory +disabled-hover text. No font shrinking, icon substitution, units, camera/lab +dispatch or scientific semantics change. Pinned egui's existing selectable-value +helper uses the same selectable-button implementation, so keyboard/focus and +named button behavior remain on that standard widget path. One minor response +semantics recommendation was sent: preserve its clicked-and-value-changed +`mark_changed()` behavior when returning the helper's responses. + +Completely inspected the one exact probe: +`viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent`. +It creates a local Context, sets pixels-per-point, paints two inert SidePanels and +the mode-tab helper in a CentralPanel. It uses only a literal ViewMode and local +UI responses, iterating 1×/1.5× scale and blocked/unblocked states for two frames. +There is no Viewport/default/show, App, T4 handle, camera, lab, model constructor, +solver/evaluator, history, worker, runtime, recording, filesystem or cleanup path. +Only this individually enumerated exact test is admitted by this inspection. + +The geometry checks model a 900×650 physical-size canvas with logical dimensions +divided by scale, reserve 280px setup and 64px collapsed-scene panels, and assert +whole-button horizontal containment, bounded per-button/row height, disabled +policy and remaining canvas height. They establish local egui layout conditions, +not an actual native window, OS scaling, complete viewport header with every +additional control, pointer activation or screen-reader acceptance. + +The generated header-only patch contains only the tab helper, replacement of the +old mode-loop and this test module. It excludes T4 fields, handle logic and +ViewInputs changes. Read-only `git apply --check --cached` passed against the +clean HEAD index. Checking against the working file fails because that file +already contains the repair; that outcome is not a base-patch failure and no +patch was applied by the reviewer. Final packet identity and retained probe/static +evidence will be recorded below. Do not attribute a whole working-file hash to +the isolated repair while T4 changes are also present. + +No blocking source finding was identified for this bounded layout fix. Native +verification on its actual committed source remains required; no T4 interaction +or force/runtime acceptance is implied. + +## Final source isolation receipt; evidence pending + +The response-semantics recommendation is implemented: only a click that changes +the mode assigns it and marks the response changed, matching pinned egui's +selectable-value behavior. This adds no new helper/test path. + +The final header-only patch is +`/tmp/rheon-ui-header-only-reviewed3.patch`, SHA256 +`901622bd12cbb09fc5729547ba3c108c85f0dd6abcfc026b9d6bc9139a998a44`. +Independently inspected the staged diff: it contains only the helper, mode-loop +replacement and test module, with no T4 field, ViewInputs or handle changes. +The staged viewport blob, `/tmp/rheon-ui-header-only.rs` and isolated +`/workspace/Rheon-ui-header-verify/src/bin/desktop/viewport.rs` have identical +SHA256 `abbaf658447dcc946fa8c51fe48d6c9597af8f80da4592b220152820042f1eb8`. +The isolated worktree differs from parent only in that file's header repair; +its whitespace check is clean. Reviewer did not stage/apply a patch or mutate +that worktree. + +The earlier shared-worktree exact log reports one passing responsive-layout test, +but is not being used as isolated final gate evidence while T4 is present there. +Final isolated list/exact/static results remain pending in this receipt. Native +and subsequent synthetic-presentation harness work are separate evidence tasks. + +## Final isolated evidence + +Verified `../evidence/responsive-header-20261020/manifest.json`, SHA256 +`dfbe770f6d49ad576b1c73910472530bdda4696e17741ddc66da9f7702757ee8`. +All six retained logs match declared byte counts/SHA256 and their original `/tmp` +bytes. The exact named probe occurs once in the isolated 183-test enumeration; +its isolated result reports one pass, zero failures/ignored and 182 filtered out. +The isolated source, temporary repair source and staged blob remain identical +at the source seal above; the reviewed patch seal also matches. This final +receipt supersedes the evidence-pending status and does not use T4 as gate evidence. + +Read isolated locked-check and strict-Clippy completion logs (7.12 and 10.95 +seconds). Format/whitespace logs are empty, consistent with the implementation +owner's successful-exit receipt. Reviewer performed read/hash checks only and +did not rerun any gate. No blocking source/evidence finding remains for this +separate repair checkpoint. Native verification remains pending on its actual +committed source; no synthetic harness, complete header navigation, T4 interaction, +scientific runtime or publication outcome is inferred. diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index 4527ea6c..c4d62def 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -52,6 +52,14 @@ enum ViewMode { Obstacle, Transport, } +// Whole labelled tabs wrap between controls, never within a label. +fn view_mode_tabs(ui: &mut egui::Ui, current: &mut ViewMode, blocked: bool) -> [egui::Response; 4] { + [(ViewMode::Appearance,"Appearance"),(ViewMode::Particles,"Particles"),(ViewMode::Obstacle,"Obstacle lab"),(ViewMode::Transport,"Transport session")].map(|(mode,label)|{ + let mut response=ui.add_enabled(!blocked||mode==ViewMode::Appearance,egui::Button::selectable(*current==mode,label).wrap_mode(egui::TextWrapMode::Extend)); + if response.clicked() && *current!=mode {*current=mode;response.mark_changed();} + response.on_disabled_hover_text("Authored-force preview and lab runs are not integrated. Use the scene list and properties to author the draft.") + }) +} pub struct Viewport { /// Draft authored fields have no runtime/preview integration yet. pub authored_forces_pending: bool, @@ -140,16 +148,7 @@ impl Viewport { self.mode = ViewMode::Appearance; } ui.horizontal_wrapped(|ui| { - for (mode, label) in [ - (ViewMode::Appearance, "Appearance"), - (ViewMode::Particles, "Particles"), - (ViewMode::Obstacle, "Obstacle lab"), - (ViewMode::Transport, "Transport session"), - ] { - ui.add_enabled_ui(!self.authored_forces_pending || mode == ViewMode::Appearance, |ui| { - ui.selectable_value(&mut self.mode, mode, label); - }).response.on_disabled_hover_text("Authored-force preview and lab runs are not integrated. Use the scene list and properties to author the draft."); - } + view_mode_tabs(ui, &mut self.mode, self.authored_forces_pending); if self.mode != ViewMode::Appearance { ui.checkbox(&mut self.engineering_camera.orthographic, "Orthographic"); if ui.button("Frame view").clicked() { @@ -1009,3 +1008,67 @@ mod tests { assert!(viewport.camera.distance < distance); } } + +#[cfg(test)] +mod header_ui_tests { + use super::*; + #[test] + fn responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent() { + for scale in [1.0, 1.5] { + for blocked in [false, true] { + let ctx = egui::Context::default(); + ctx.set_pixels_per_point(scale); + let mut mode = ViewMode::Appearance; + for _ in 0..2 { + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(900.0 / scale, 650.0 / scale), + )), + ..Default::default() + }, + |ctx| { + egui::SidePanel::left("settings") + .exact_width(280.0) + .show(ctx, |ui| { + ui.label("Setup"); + }); + egui::SidePanel::right("scene-hidden") + .exact_width(64.0) + .show(ctx, |ui| { + let _ = ui.button("Scene"); + }); + egui::CentralPanel::default().show(ctx, |ui| { + let available = ui.available_rect_before_wrap(); + let row = ui.horizontal_wrapped(|ui| { + view_mode_tabs(ui, &mut mode, blocked) + }); + assert!( + row.response.rect.height() <= 60.0, + "tab rows too tall: {:?}", + row.response.rect + ); + for (i, response) in row.inner.iter().enumerate() { + assert!( + response.rect.height() + <= ui.spacing().interact_size.y + 1.0, + "label wrapped vertically" + ); + assert!( + response.rect.left() >= available.left() - 1.0 + && response.rect.right() <= available.right() + 1.0, + "whole tab outside header" + ); + assert_eq!(response.enabled(), !blocked || i == 0); + } + assert!(ui.available_height() > available.height() * 0.7); + }); + }, + ); + } + assert!(mode == ViewMode::Appearance); + } + } + } +} From 425a9869fa3f36b7e3133ddbd213a493d7b5bab3 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 23:39:24 +0000 Subject: [PATCH 12/25] Add transactional draft force transform and support handles --- .../force-handles-20261020/.gitattributes | 1 + .../force-handles-20261020/manifest.json | 146 +++ .../rheon-ui-handles-accepted-check.log | 2 + .../rheon-ui-handles-accepted-clippy.log | 2 + .../rheon-ui-handles-accepted-diff.log | 0 .../rheon-ui-handles-accepted-fmt.log | 0 .../rheon-ui-handles-accepted-list.log | 194 ++++ ...ointer_and_camera_loss_never_hands_off.log | 8 + ...n_units_and_nonidentity_local_rotation.log | 8 + ...rrow_without_touching_authoring_values.log | 8 + ...ort_release_restores_base_and_snapshot.log | 8 + ...once_and_escape_consumes_until_release.log | 8 + ...l_position_never_commits_stale_preview.log | 8 + ...eserves_retained_index_without_history.log | 8 + ...projects_disabled_run_without_dispatch.log | 8 + ...d_exhaustion_preserve_canonical_values.log | 8 + ..._are_visible_without_snapshot_mutation.log | 8 + ...ect_edit_collapse_and_retained_context.log | 8 + ...ry_depth_near_clip_and_degenerate_hits.log | 8 + ...eserve_zero_direction_sign_and_support.log | 8 + ...ds_exact_seams_and_refuses_nonmanifold.log | 8 + ...erlap_retained_and_unavailable_preview.log | 8 + ...entity_ignores_drag_and_modified_click.log | 8 + .../rheon-ui-handles-final-clippy.log | 35 + .../rheon-ui-handles-list4.log | 9 + docs/plans/ui-refinement/execution-ledger.md | 29 + docs/plans/ui-refinement/plan.md | 6 +- .../reports/force-handles-source-review.md | 180 +++ .../reports/native-force-handles-handoff.md | 41 + src/bin/desktop/scene_handles.rs | 1002 +++++++++++++++++ src/bin/desktop/scene_ui.rs | 144 ++- src/bin/desktop/scene_view.rs | 25 +- src/bin/desktop/viewport.rs | 46 +- src/bin/rheon-desktop.rs | 3 + 34 files changed, 1971 insertions(+), 22 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-check.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-diff.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-list.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-final-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-list4.log create mode 100644 docs/plans/ui-refinement/reports/force-handles-source-review.md create mode 100644 docs/plans/ui-refinement/reports/native-force-handles-handoff.md create mode 100644 src/bin/desktop/scene_handles.rs diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/.gitattributes b/docs/plans/ui-refinement/evidence/force-handles-20261020/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/manifest.json b/docs/plans/ui-refinement/evidence/force-handles-20261020/manifest.json new file mode 100644 index 00000000..b5525b23 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/manifest.json @@ -0,0 +1,146 @@ +{ + "parent": "8504b3e7c9e6a02a6e2521b54c1aefc9c0f58ec2", + "exact_test_names": [ + "scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation", + "scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview", + "scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release", + "scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot", + "scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off", + "scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values", + "scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values", + "scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context", + "scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation", + "scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch", + "scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history", + "scene_view::tests::geometry_depth_near_clip_and_degenerate_hits", + "scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support", + "scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold", + "scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview", + "scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click" + ], + "source_sha256": { + "src/bin/desktop/scene_handles.rs": "8c2b0a78283aa8948daaeac5accc1d1c642c2af689cc5d85731df602192556db", + "src/bin/desktop/scene_ui.rs": "5df230c1da9b0c976ea8839e564e6db0ac95bbc6c965ad20cbf6ae13ec069cc2", + "src/bin/desktop/scene_view.rs": "ab55f9a7e5d439211b1c4a4145d9c43adfda15748301055739df5c9251348572", + "src/bin/desktop/viewport.rs": "ccdedbdfcd49740a11c5855bdf48267e3645e60c9d6e4475b9c854359db7a2c6", + "src/bin/rheon-desktop.rs": "6388514fb5a44f43c0c699534e76487f9f7ee527159b701e72265504d1784fab" + }, + "logs": { + "rheon-ui-handles-accepted-check.log": { + "sha256": "d3cc552084ac0ffa10df1bc473ea1a7e6c8c97712b638b35e456f494405ad28a", + "bytes": 130, + "source": "/tmp/rheon-ui-handles-accepted-check.log" + }, + "rheon-ui-handles-accepted-clippy.log": { + "sha256": "5e64f21eb14f3450713cc83e0b229228137d11d114ca0a469ac5a7183bf89b4c", + "bytes": 130, + "source": "/tmp/rheon-ui-handles-accepted-clippy.log" + }, + "rheon-ui-handles-accepted-diff.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-handles-accepted-diff.log" + }, + "rheon-ui-handles-accepted-fmt.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-handles-accepted-fmt.log" + }, + "rheon-ui-handles-accepted-list.log": { + "sha256": "96666448e86eded2bb540f17e5ad0d8b4841d6ad46f4f1a1968eac2b7979bbe5", + "bytes": 19983, + "source": "/tmp/rheon-ui-handles-accepted-list.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log": { + "sha256": "8024fc20505da490b4fddcd579a879872fc41aa80ebf3c9885adff676d55f874", + "bytes": 419, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log": { + "sha256": "b0f3d01d5eaf82d675b0109f27cc1ae08578e767a829f57e2acc87e0041779d4", + "bytes": 414, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log": { + "sha256": "ba7fe7cb11a96336cef34817947e4c176f603c016c1e473825543d7012583f62", + "bytes": 412, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log": { + "sha256": "bc52281d5767abbfbbb314a1f7015bb44445f0e045fba847dbcc8f666ec88542", + "bytes": 411, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log": { + "sha256": "1929082402ea0627894ad61ef6a36fe8856ca77f7b70dfe5c7f9aa79ca8b8da0", + "bytes": 415, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log" + }, + "rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log": { + "sha256": "66cc08d468d751534581f9aa7af93a01daea491158467a6c603743205731d590", + "bytes": 415, + "source": "/tmp/rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log" + }, + "rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log": { + "sha256": "dc9756a7e0ba3436be2294d8301fb0d05218c122d045d4111d8b9fee71f97c42", + "bytes": 411, + "source": "/tmp/rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log" + }, + "rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log": { + "sha256": "5214f2c9a7bd387f8bc385a63ccf285e313d334d1adc2b8594d9191a28e0f2a6", + "bytes": 398, + "source": "/tmp/rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log" + }, + "rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log": { + "sha256": "27d50205f5333945f9c5540098d1aa15a5fde547de42cbbd1c902a4a8e2821f1", + "bytes": 411, + "source": "/tmp/rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log" + }, + "rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log": { + "sha256": "bab04e3c63dded516a21eb6ea866dcbd8929d210d218f9ea5af9ed035f61affb", + "bytes": 412, + "source": "/tmp/rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log" + }, + "rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log": { + "sha256": "ba21f97e233c4b98afe5adb2830f217a88a60afd6ce20d43966daf1edaeafb29", + "bytes": 399, + "source": "/tmp/rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log" + }, + "rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log": { + "sha256": "2a33e529bd306132506d3fe702c105e5605c39b97cf1b0e1f8ec8f36e9594b00", + "bytes": 386, + "source": "/tmp/rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log" + }, + "rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log": { + "sha256": "479a02e21f06ca91356ddadcba3692a72775a2fbd0e1940a97c9cfb26bee884f", + "bytes": 404, + "source": "/tmp/rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log" + }, + "rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log": { + "sha256": "4572efc450d217e8c564217261ffd15c473e7f2c6688e7b3f3311bcaa6edfece", + "bytes": 410, + "source": "/tmp/rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log" + }, + "rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log": { + "sha256": "f0fdb67659ed543b3b14d80b8738b450f2bb7286d1958b5021a53a329ff60d33", + "bytes": 402, + "source": "/tmp/rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log" + }, + "rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log": { + "sha256": "5a87824d9ac799c5c551f12838cf33f397a4b07f363ef261b8779d67be1f1fbf", + "bytes": 409, + "source": "/tmp/rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log" + }, + "rheon-ui-handles-final-clippy.log": { + "sha256": "e700750e4580978dd53fbbcdb8eab9988cea4b863672a095a31dd5bb5352c23d", + "bytes": 1380, + "source": "/tmp/rheon-ui-handles-final-clippy.log" + }, + "rheon-ui-handles-list4.log": { + "sha256": "5073c4bee51148aa7dd3d416128117a4e0115c2cfc497f8c80d1a2e33c73419f", + "bytes": 449, + "source": "/tmp/rheon-ui-handles-list4.log" + } + }, + "boundary": "inert UI/geometry only; no solver/model/evaluator/workers/IO fixtures/native display" +} diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-check.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-check.log new file mode 100644 index 00000000..b842055b --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.08s diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-clippy.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-clippy.log new file mode 100644 index 00000000..8bce9b20 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 5.29s diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-diff.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-diff.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-fmt.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-list.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-list.log new file mode 100644 index 00000000..8652b8d2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-list.log @@ -0,0 +1,194 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 4.81s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +189 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log new file mode 100644 index 00000000..c8ecea93 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.03s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log new file mode 100644 index 00000000..48a8cf85 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_geometry_ray_translation_units_and_nonidentity_local_rotation.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log new file mode 100644 index 00000000..5e3a30da --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_named_controls_fit_narrow_without_touching_authoring_values.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log new file mode 100644 index 00000000..2296ea2b --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_invalid_support_release_restores_base_and_snapshot.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log new file mode 100644 index 00000000..a3375c87 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_pointer_release_commits_once_and_escape_consumes_until_release.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log new file mode 100644 index 00000000..87c5a0a8 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_handles-tests-handles_transaction_invalid_final_position_never_commits_stale_preview.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log new file mode 100644 index 00000000..36d17b39 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_draft_timeline_roundtrip_preserves_retained_index_without_history.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log new file mode 100644 index 00000000..06ed1278 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_force_refusal_projects_disabled_run_without_dispatch.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log new file mode 100644 index 00000000..c1552ba9 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_identity_context_removal_and_exhaustion_preserve_canonical_values.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log new file mode 100644 index 00000000..a8f44f20 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log new file mode 100644 index 00000000..01eaf5d4 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_ui-tests-scene_pointer_add_select_edit_collapse_and_retained_context.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.11s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log new file mode 100644 index 00000000..d58f255e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_depth_near_clip_and_degenerate_hits.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.23s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_depth_near_clip_and_degenerate_hits ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log new file mode 100644 index 00000000..22d72902 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_force_glyphs_preserve_zero_direction_sign_and_support.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log new file mode 100644 index 00000000..5b1d37fd --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log new file mode 100644 index 00000000..323450b7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pick_identity_overlap_retained_and_unavailable_preview.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log new file mode 100644 index 00000000..75c4fb38 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-accepted-scene_view-tests-scene_pointer_selects_same_identity_ignores_drag_and_modified_click.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 188 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-final-clippy.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-final-clippy.log new file mode 100644 index 00000000..6648db5d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-final-clippy.log @@ -0,0 +1,35 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error: unneeded late initialization + --> src/bin/desktop/scene_handles.rs:155:9 + | +155 | let endpoint; + | ^^^^^^^^^^^^^ + | + = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#needless_late_init + = note: `-D clippy::needless-late-init` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_late_init)]` +help: move the declaration `endpoint` here and remove the assignments from the branches + | +155 ~ +156 | let mut line = |a: V, b: V| { +... +162 | }; +163 ~ let endpoint = if tool == Tool::Rotate { +164 | let b = axis(object, tool, (d + 1) % 3); +... +175 | } +176 ~ normalized(point(0), side) +177 | .and_then(|p| camera.project(p, rect)) +178 | .map(|p| p.0) +179 ~ .filter(|p| p.is_finite() && rect.contains(*p)) +180 | } else { +... +191 | line(center, end); +192 ~ normalized(end, side) +193 | .and_then(|p| camera.project(p, rect)) +194 | .map(|p| p.0) +195 ~ .filter(|p| p.is_finite() && rect.contains(*p)) +196 ~ }; + | + +error: could not compile `rheon` (bin "rheon-desktop") due to 1 previous error diff --git a/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-list4.log b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-list4.log new file mode 100644 index 00000000..ab1e36bf --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-handles-20261020/rheon-ui-handles-list4.log @@ -0,0 +1,9 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error[E0063]: missing field `near_parameter` in initializer of `scene_handles::Ray` + --> src/bin/desktop/scene_handles.rs:86:20 + | +86 | .then_some(Ray { origin, direction }) + | ^^^ missing `near_parameter` + +For more information about this error, try `rustc --explain E0063`. +error: could not compile `rheon` (bin "rheon-desktop" test) due to 1 previous error diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index b6171d0f..f753ce00 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -389,3 +389,32 @@ authorized for retained/timeline behavior. It must contain a legal frozen Reques two ordered literal preview frames with distinct timestamps and two tracks, label synthetic data, and remain absent from production launch. No implementation or acceptance for that harness is claimed in this header checkpoint. + +## T4 force handle source checkpoint —2026-10-20 + +Follows distinct8504b3e header repair on PR146. Canonical force objects have named +Move/Rotate/Size tools, world translation/local rotation/local support axes with +thin selected outline; models remain read-only. SceneUi owns one validated draft +transaction. During drag only an explicit preview changes; release revalidates in +current scene and commits once. Invalid final pointer, Escape, focus/pointer/rect/ +camera/domain/context/identity/base conflict cancels. Capture and cancellation +latch consume terminal frame through explicit release, so the same gesture cannot +fall through to camera/picking. Fast same-frame press/release clears cleanly. +Untouched translation axes/signed zero and zero-motion base retain exact bits. +Camera shortcuts respect field focus. Historical/frozen runs remain untouched. + +Independent source review repaired7 findings. Final source gates:189-name list, +16 individually exact inspected inert tests (6handles,5scene,5picking) passed, strict +production Clippy and locked offline desktop check, fmt/diff passed. Manifest +`evidence/force-handles-20261020/manifest.json` SHA +ca49f7b8eee55b8ba2e5ecf5745c6f6ad8d763e6893860934254c3a207ccd261 contains5source +seals,21accepted logs and2preserved compile/style failure logs. Intermediate list4 +failed on an incomplete Ray initializer; final Clippy rejected a local late-init +style before repair. Both corrected before final accepted source/checks; no test or +numerical failure is hidden. Earlier intermediate inert UI passes remain in /tmp. + +No native display, full Viewport gesture-dispatch run, assistive usability or actual +model manipulation/force runtime/archive test occurred here. Native handoff: +reports/native-force-handles-handoff.md. Force-enabled run refusal remains. Next: +separately reviewed workerless synthetic retained/timeline presentation fixture, +then agreed T5 runtime/immutable archive custody; no scientific workload authorized. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 3c4e2660..e71ba619 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T3 source checkpoint;6127 native validation separate -**Next slice:** T4 force translation/rotation/support handles +**Current phase:** T4 bounded source checkpoint; native validation separate +**Next slice:** workerless native presentation harness, then T5 runtime/archive custody **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -97,7 +97,7 @@ Inspector slice before any overlapping changes; T1 excludes Inspector entirely. | T1 | main timeline presentation and desktop/timeline_ui_tests.rs; bounded labelled speed editor, ordinary invisible local measurement, unchanged physical context | Four inspected exact UI probes + compile/Clippy/fmt + independent source review; native remains separate | Completed (bounded source) | | T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests; viewport.rs mode-control guard only (no lab dispatch/picking changes) | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; viewport mode refusal only, no runtime dispatch | Completed (bounded source) | | T3 | new scene_view.rs geometry/picking; viewport.rs aggregate-input composition and thin geometry outline; existing SceneUi selection and main routing | [Exact selection admission](reports/viewport-selection-admission.md), inert camera/geometry/UI tests; no lab initialization, then native | Bounded source complete; native pending | -| T4 | desktop/scene_handles.rs, viewport exclusive gesture routing, scene_ui validated draft transaction; force-only (model placement deferred) | [Force-only handle admission](reports/force-handles-admission.md); same IDs/domain types; no physics; exact Escape rollback and terminal-frame ownership | Planned | +| T4 | desktop/scene_handles.rs, viewport exclusive gesture routing, scene_ui validated draft transaction; force-only (model placement deferred) | [Force-only handle admission](reports/force-handles-admission.md); same IDs/domain types; no physics; exact Escape rollback and terminal-frame ownership | Bounded source complete; native pending | | T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | diff --git a/docs/plans/ui-refinement/reports/force-handles-source-review.md b/docs/plans/ui-refinement/reports/force-handles-source-review.md new file mode 100644 index 00000000..15e0e107 --- /dev/null +++ b/docs/plans/ui-refinement/reports/force-handles-source-review.md @@ -0,0 +1,180 @@ +# Independent T4 force-handle source review + +2026-10-20. Review in `/workspace/Rheon-ui-authoring`, preserving separately +committed responsive-header parent `8504b3e7c9e6a02a6e2521b54c1aefc9c0f58ec2` +and T3 evidence. Scope is `scene_handles.rs`, the canonical SceneUi transaction, +shared scene geometry/composition, viewport capture ownership and main editability. +Reviewed against force-handles admission and independent plan conditions. Only +this report is authored; no tests, builds, native UI, model/solver/evaluator, +runtime/history/recording, process, filesystem fixture or cleanup was executed +by the reviewer. + +## Complete inert test boundary + +Inspected these six full `scene_handles::tests` paths and all called helpers: + +- `handles_geometry_ray_translation_units_and_nonidentity_local_rotation` +- `handles_transaction_invalid_final_position_never_commits_stale_preview` +- `handles_pointer_release_commits_once_and_escape_consumes_until_release` +- `handles_pointer_invalid_support_release_restores_base_and_snapshot` +- `handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off` +- `handles_named_controls_fit_narrow_without_touching_authoring_values` + +The fixture constructs literal canonical ForceObjects/ForceScenes, a value-only +SceneUi test fixture and OrbitCamera. The harness creates a local Context and +canvas response, calls Handles.update/paint/controls directly and inspects its +state. It never constructs Viewport/App, calls their update/input/show, initializes +a lab, imports/constructs CollisionMesh, samples a field, creates a numerical +history, worker, recording or output file. Freeze is validation and immutable +deep-copy only. In-memory Serde JSON encoding for source comparison does not +perform IO. Ray/axis/plane/quaternion arithmetic is UI geometry, not numerical +simulation evidence. The narrow-controls helper paints only the toolbar and +asserts authoring values remain unchanged. + +The five previously inspected scene_ui and five scene_view exact probes remain +within their inert boundaries. The extended scene-invalid-support test only +adds literal draft mutations, preview/release/freeze and signed-zero source +comparison. No broad suite/filter or uninspected helper is admitted. The intended +runtime selection is six handle plus those ten fully qualified names, each +list-enumerated and run individually with `--exact` by the implementation owner. + +## Canonical ownership and geometry + +SceneUi owns the draft, selected ID and optional transaction. The transaction +retains the exact source object, a validated last preview and separate current +validity; invalid current input cannot commit a stale valid preview. Canonical +values stay unchanged during drag. The display is a temporary Cow projection, +not a second mutable force owner or an updated run snapshot. Release checks +selection/context and source equality, including signed-zero distinction through +bounded in-memory JSON bytes. Final replacement revalidates the candidate in the +current containing document, preserving newer unrelated edits on refusal. + +Translation uses world XYZ and metres; rotation/support use the base orientation's +local axes. Each candidate derives from the exact original object and original +ray parameter/vector, avoiding accumulated frame deltas. Ray inversion matches +the camera's orthographic/perspective scale. The closest-axis and ray-plane +formulae use normalized directions and reject parallel/central degeneracy. +World-axis delta quaternion multiplication against a nonidentity base preserves +the intended local-axis edit order; the literal rotated-box resize exercises the +same local support convention. Radius and selected box half-extent are edited +without clamping invalid sizes. Global has no size handle; its uniform-directional +anchor limitation remains in help. Models and retained runs are not manipulated. + +Viewport gives a capture attempt first ownership, then ordinary camera input and +T3 picking only if the frame is unconsumed. Capture/latch forces Appearance and +disables independent view modes. Escape and lost focus/pointer/context/source/ +camera/canvas/domain cancel without rolling an old base over newer data. Every +terminal/cancel frame stays consumed; explicit primary release clears the latch. +Wheel/Home/zoom and object picks cannot receive that owned frame. Numeric +properties remain the keyboard alternative and are disabled during an active edit; +add/remove is likewise disabled. Main passes idle-worker editability, while +selected_force limits candidates to the Draft context and canonical force IDs. + +## Findings tracked during source review + +1. **Repaired:** Hit admission originally ran before the same validation that + suppressed painting, allowing invisible invalid-draft handles to capture. + It now checks the validated display scene before a new hit. +2. **Repaired:** Coalesced primary press/release could create an orphaned capture. + It now consumes that click frame, preserves exact base and finishes without + leaving a drag/latch. A literal same-frame event assertion was added. +3. **Repaired:** Release originally trusted cached validity. It now freezes the + preview inserted into the current full scene before canonical replacement; + pure assertions cover an unrelated invalid object at release. +4. **Repaired:** Vector addition during world translation rewrote untouched signed + zeros. Translation now edits only the selected component and returns the exact + base for zero delta; zero-angle rotation likewise returns base. Literal bit + assertions and a signed-zero source-conflict guard cover these cases. +5. **Repaired:** A tree/context change before viewport update could briefly project + the old transaction under a different selected row. Cow and Properties now + substitute the preview only while edit_matches is true; the new selected + canonical object remains authoritative while Handles cancels/latches its + captured gesture. A literal stale-selection display assertion was added. +6. **Repaired:** Solve admission originally rejected only intersections behind + the ray origin. Ray now carries the camera-forward near-plane parameter; + axis and plane solves reject forward intersections before that threshold. + Positive-but-before-near literal cases extend the existing geometry test. +7. **Repaired:** Axis labels at endpoint+(7,-7) could disappear at top/right + canvas boundaries. Labels are now measured and clamped within the canvas, + preserving named axes alongside their colours. + +The final harness extends the same cancellation test with domain-change +cancellation, continuation/release outside the canvas and Middle non-admission, +in addition to rect, focus/pointer/context/camera/editability loss. These additions +remain local geometry/Context paths. The changed-domain case checks cancellation; +final harness painting also receives the fixture's current side, but no +changed-domain visual geometry is asserted. Native full-viewport gesture suppression +is not established by the direct Handles harness, which records a consumed flag +rather than invoking a camera. Actual native focus recovery, contrast, high-DPI +targets and assistive usability remain separate gates. Model movement and force +runtime/scientific acceptance remain outside this slice. + +## Source disposition before final evidence + +No blocking source finding remains after the seven repairs above. The complete +six handle paths and previously inspected five SceneUi/five SceneView paths are +admitted only as individually enumerated exact UI/geometry probes. Current +source inspection does not promote the earlier intermediate pass logs to final +sealed evidence. No test or build was executed by this reviewer. + +| Reviewed source | SHA256 | +| --- | --- | +| `src/bin/desktop/scene_handles.rs` | `8c2b0a78283aa8948daaeac5accc1d1c642c2af689cc5d85731df602192556db` | +| `src/bin/desktop/scene_ui.rs` | `5df230c1da9b0c976ea8839e564e6db0ac95bbc6c965ad20cbf6ae13ec069cc2` | +| `src/bin/desktop/scene_view.rs` | `ab55f9a7e5d439211b1c4a4145d9c43adfda15748301055739df5c9251348572` | +| `src/bin/desktop/viewport.rs` | `ccdedbdfcd49740a11c5855bdf48267e3645e60c9d6e4475b9c854359db7a2c6` | +| `src/bin/rheon-desktop.rs` | `6388514fb5a44f43c0c699534e76487f9f7ee527159b701e72265504d1784fab` | + +Pending final evidence must retain the exact enumerated names/source seals and +attribute actual checks separately from native usability. The source paths do +not mutate core force semantics, numerical arrays, Request, frozen run intent, +recording/archive or model Apply. Reviewer changed only this report and the +read-only whitespace check is clean. + +## Final retained evidence and disposition + +Independently verified +`../evidence/force-handles-20261020/manifest.json`, SHA256 +`ca49f7b8eee55b8ba2e5ecf5745c6f6ad8d763e6893860934254c3a207ccd261`. +All five source seals match the current files above. All **23** retained logs +match declared sizes/SHA256 and original `/tmp` bytes: 21 accepted logs +(16 exact probes plus enumeration/check/Clippy/fmt/diff), and two preserved +compile/style failures. Log attributes disable text conversion. These checks +verify the packet's identity, not a signed binary provenance certificate. + +The final accepted enumeration contains 189 tests. Each of the 16 distinct +inspected names occurs once in it and matches one retained passing exact log; +each reports one selected pass, zero failures/ignored and 188 filtered out. +No test was run by the reviewer. This is focused local UI/geometry evidence, +not evidence of a model, solver, evaluator, worker or recorded run. + +The compile-only list4 attempt failed because the newly added near-plane Ray +initializer initially omitted its new field. That initializer was repaired before +the final accepted compilation/execution. The first strict-Clippy attempt rejected +late initialization of `endpoint`. Re-read the final change: both branch bodies +and geometry are unchanged, with the result now expressed as +`let endpoint = if ... { ... } else { ... };`. The final source identity in the +table includes that style repair. Both earlier failures are retained and neither +is represented as a passing gate. + +Accepted locked check and strict Clippy complete in 1.08 and 5.29 seconds. +Format/diff logs are empty, consistent with the implementation owner's successful +exit receipt; empty text alone is not an independent exit-status record. Reviewer +performed read/hash checks and `git diff --check`, which was clean, and authored +only this report. No build/test/publication operation was performed by reviewer. + +No blocking source or retained-evidence finding remains for this distinct T4 +checkpoint. It is suitable for the existing scoped review/publication workflow, +while preserving the separate header/T3 history and the explicit limitations +below. This is not complete native or scientific feature acceptance. + +The direct harness demonstrates consumed flags and canonical transactions; it +does not execute full Viewport camera/picking fall-through or an actual active +worker. Native focus recovery when the OS loses a release event, mixed gestures, +high-DPI handle targets/contrast, canvas-edge labels, full property focus order +and assistive usability still require current-revision inspection. Actual model +movement/Apply, immutable-run interaction and authored force runtime/archive +propagation remain unavailable or separately unverified. Existing numerical, +archive-integrity and recording-completeness claims are not expanded by these +authoring tests. The separately authorized synthetic native-presentation harness +is a later task, not evidence supplied by this receipt. diff --git a/docs/plans/ui-refinement/reports/native-force-handles-handoff.md b/docs/plans/ui-refinement/reports/native-force-handles-handoff.md new file mode 100644 index 00000000..ae56330a --- /dev/null +++ b/docs/plans/ui-refinement/reports/native-force-handles-handoff.md @@ -0,0 +1,41 @@ +# Native T4 force handle handoff + +Verify exact published T4 source/parent/tree and evidence manifest before building. +Preserve6127 idle and60f3498 picking and8504b3e header receipts. No Run, rerun, +comparison, numerical lab, model import or scientific workload. Use only idle Draft +force authoring. No genuine worker cancellation or runtime/archive acceptance. + +At1250×820/900×650 and100/150% scale, open Scene. Add/select each default zero force; +neutral anchor remains visible with a thin selected outline. Move/Rotate/Size are +named tools; world translation vs local rotation/size, labelled X/Y/Z and units. +Default Global has no size boundary; select Sphere/Box in properties. Marker length +is an authoring aid, not field magnitude. A Global Directional anchor move does not +change its uniform physical field. Model transforms remain explicitly outside this +force-only slice; existing model properties stay read-only. + +Move: primary-press endpoint, drag each visible axis, inspect disabled numeric preview +and release to commit once. Verify untouched coordinate digits/units; clicking without +motion preserves values. During drag, wheel/Home/zoom/mode changes must not move camera. +Escape cancels exact base and consumes gesture until release; same terminal frame must +not select/clear another object or orbit camera. Shift/Ctrl/middle gestures away from +handles retain camera ownership. A geometrically edge-on handle visibly refuses with +“change view or use properties” and consumes attempted drag. Numeric fields remain the +keyboard alternative; canvas focus starts only on a handle/pick click. + +Rotate: use all local rings on a nonidentity orientation; confirm direction, local axis +and property quaternion/Euler orientation agree. Size: sphere radius and rotated Box +half-extents use metres. Valid move then drag below zero then release must cancel the +whole edit, not commit stale valid preview. Current preview and release are validated; +no silent clamping/normalization. Continue/release outside canvas; focus loss, pointer +loss, Scene collapse/resize, source/selection/context/domain changes cancel safely and +retain pointer ownership until release. Confirm ordinary camera/picking returns after +release. The unit tests do not execute full Viewport camera dispatch or native focus. + +Check long labels and controls at narrow scale; selection names/axis labels remain +inside canvas, errors remain visible below measured viewport info. Capture actual +quality85JPEGs with exact source revision/size/scale/state. Retained/draft playback +checks need the separately reviewed workerless synthetic harness; do not create a run +or treat archive viewer DBs as reconstructed App scenes. Preserve synthetic annotation. + +No native gesture/contrast/high-DPI/assistive usability repair pass is claimed by this +owner (DISPLAY/WAYLAND_DISPLAY unavailable). Authored-force runtime remains refused. diff --git a/src/bin/desktop/scene_handles.rs b/src/bin/desktop/scene_handles.rs new file mode 100644 index 00000000..4ce64b5e --- /dev/null +++ b/src/bin/desktop/scene_handles.rs @@ -0,0 +1,1002 @@ +//! Draft-only geometric manipulators. No solver or field evaluator calls. +use super::{ + camera::OrbitCamera, + scene_ui::SceneUi, + scene_view::{Line, line_hit, normalized, rotate, segment}, +}; +use eframe::egui::{self, Color32, Pos2, Rect, Stroke}; +use rheon::{ForceObject, ForceSupport}; +type V = [f64; 3]; +fn add(a: V, b: V) -> V { + std::array::from_fn(|d| a[d] + b[d]) +} +fn sub(a: V, b: V) -> V { + std::array::from_fn(|d| a[d] - b[d]) +} +fn mul(a: V, s: f64) -> V { + a.map(|v| v * s) +} +fn dot(a: V, b: V) -> f64 { + a.into_iter().zip(b).map(|(a, b)| a * b).sum() +} +fn cross(a: V, b: V) -> V { + [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ] +} +fn unit(a: V) -> Option { + let n = dot(a, a).sqrt(); + (n.is_finite() && n > 1e-12).then(|| mul(a, 1.0 / n)) +} +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +enum Tool { + #[default] + Translate, + Rotate, + Support, +} +impl Tool { + fn label(self) -> &'static str { + match self { + Self::Translate => "Move", + Self::Rotate => "Rotate", + Self::Support => "Size", + } + } +} +#[derive(Clone, Copy)] +struct Ray { + near_parameter: f64, + origin: V, + direction: V, +} +fn ray(camera: &OrbitCamera, rect: Rect, p: Pos2) -> Option { + if !p.is_finite() + || !rect.is_finite() + || rect.height() <= 0.0 + || !camera.distance.is_finite() + || camera.distance <= 0.0 + { + return None; + } + let (eye, forward, right, up) = camera.basis(); + let eye = eye.map(f64::from); + let f = forward.map(f64::from); + let r = right.map(f64::from); + let u = up.map(f64::from); + let dx = f64::from(p.x - rect.center().x) / f64::from(rect.height() * 0.88); + let dy = f64::from(rect.center().y - p.y) / f64::from(rect.height() * 0.88); + let (origin, direction) = if camera.orthographic { + ( + add( + eye, + mul(add(mul(r, dx), mul(u, dy)), f64::from(camera.distance)), + ), + f, + ) + } else { + (eye, add(f, add(mul(r, dx), mul(u, dy)))) + }; + let direction = unit(direction)?; + let near_parameter = 0.0151 / dot(f, direction); + (near_parameter.is_finite() && near_parameter > 0.0 && origin.iter().all(|v| v.is_finite())) + .then_some(Ray { + near_parameter, + origin, + direction, + }) +} +fn axis_parameter(ray: Ray, center: V, axis: V) -> Option { + let b = dot(axis, ray.direction); + let denominator = 1.0 - b * b; + if denominator < 1e-6 { + return None; + } + let w = sub(center, ray.origin); + let t = (b * dot(ray.direction, w) - dot(axis, w)) / denominator; + (t.is_finite() && dot(ray.direction, w) + b * t >= ray.near_parameter).then_some(t) +} +fn plane_vector(ray: Ray, center: V, axis: V) -> Option { + let denominator = dot(ray.direction, axis); + if denominator.abs() < 1e-6 { + return None; + } + let t = dot(sub(center, ray.origin), axis) / denominator; + if !t.is_finite() || t < ray.near_parameter { + return None; + } + unit(sub(add(ray.origin, mul(ray.direction, t)), center)) +} +fn quaternion_product([x, y, z, w]: [f64; 4], [a, b, c, d]: [f64; 4]) -> [f64; 4] { + [ + w * a + x * d + y * c - z * b, + w * b - x * c + y * d + z * a, + w * c + x * b - y * a + z * d, + w * d - x * a - y * b - z * c, + ] +} +fn axis(object: &ForceObject, tool: Tool, d: usize) -> V { + let mut a = [0.0; 3]; + a[d] = 1.0; + if tool == Tool::Translate { + a + } else { + rotate(object.transform.rotation, a) + } +} +struct Handle { + axis: usize, + lines: Vec, + endpoint: Option, +} +fn handles( + object: &ForceObject, + tool: Tool, + camera: &OrbitCamera, + rect: Rect, + side: f64, +) -> Vec { + let mut result = vec![]; + let center = object.transform.translation; + let count = if tool == Tool::Support { + match object.support { + ForceSupport::Global => 0, + ForceSupport::Sphere { .. } => 1, + ForceSupport::Box { .. } => 3, + } + } else { + 3 + }; + for d in 0..count { + let a = axis(object, tool, d); + let mut lines = vec![]; + let mut line = |a: V, b: V| { + if let (Some(a), Some(b)) = (normalized(a, side), normalized(b, side)) + && let Some(l) = segment(camera, rect, a, b) + { + lines.push(l); + } + }; + let endpoint = if tool == Tool::Rotate { + let b = axis(object, tool, (d + 1) % 3); + let c = axis(object, tool, (d + 2) % 3); + let point = |i: usize| { + let t = i as f64 * std::f64::consts::TAU / 48.0; + add( + center, + mul(add(mul(b, t.cos()), mul(c, t.sin())), side * 0.15), + ) + }; + for i in 0..48 { + line(point(i), point(i + 1)); + } + normalized(point(0), side) + .and_then(|p| camera.project(p, rect)) + .map(|p| p.0) + .filter(|p| p.is_finite() && rect.contains(*p)) + } else { + let length = if tool == Tool::Support { + match object.support { + ForceSupport::Sphere { radius, .. } => radius, + ForceSupport::Box { half_extent, .. } => half_extent[d], + ForceSupport::Global => 0.0, + } + } else { + side * 0.18 + }; + let end = add(center, mul(a, length)); + line(center, end); + normalized(end, side) + .and_then(|p| camera.project(p, rect)) + .map(|p| p.0) + .filter(|p| p.is_finite() && rect.contains(*p)) + }; + result.push(Handle { + axis: d, + lines, + endpoint, + }); + } + result +} +fn hit(handles: &[Handle], tool: Tool, p: Pos2, ortho: bool) -> Option { + handles + .iter() + .filter_map(|h| { + let distance = if tool == Tool::Rotate { + h.lines + .iter() + .filter_map(|&l| line_hit(l, p, ortho).map(|v| v.0)) + .min_by(f32::total_cmp) + } else { + h.endpoint.map(|e| e.distance(p)).filter(|v| *v <= 9.0) + }?; + Some((h.axis, distance)) + }) + .min_by(|a, b| a.1.total_cmp(&b.1).then(a.0.cmp(&b.0))) + .map(|h| h.0) +} +struct Drag { + start: Pos2, + base: ForceObject, + tool: Tool, + axis: V, + index: usize, + initial: f64, + initial_vector: Option, + camera: OrbitCamera, + rect: Rect, + side: f64, +} +impl Drag { + fn new( + base: ForceObject, + tool: Tool, + index: usize, + camera: &OrbitCamera, + rect: Rect, + side: f64, + p: Pos2, + ) -> Option { + if !side.is_finite() || side <= 0.0 { + return None; + } + let axis = unit(axis(&base, tool, index))?; + let center = base.transform.translation.map(|v| v / side); + let r = ray(camera, rect, p)?; + let (initial, initial_vector) = if tool == Tool::Rotate { + (0.0, Some(plane_vector(r, center, axis)?)) + } else { + (axis_parameter(r, center, axis)?, None) + }; + Some(Self { + start: p, + base, + tool, + axis, + index, + initial, + initial_vector, + camera: camera.clone(), + rect, + side, + }) + } + fn candidate(&self, p: Pos2) -> Option { + if p == self.start { + return Some(self.base.clone()); + } + let mut candidate = self.base.clone(); + let center = self.base.transform.translation.map(|v| v / self.side); + let r = ray(&self.camera, self.rect, p)?; + if self.tool == Tool::Rotate { + let v = plane_vector(r, center, self.axis)?; + let start = self.initial_vector?; + let angle = dot(self.axis, cross(start, v)).atan2(dot(start, v)); + if angle == 0.0 { + return Some(self.base.clone()); + } + let half = angle * 0.5; + let q = [ + self.axis[0] * half.sin(), + self.axis[1] * half.sin(), + self.axis[2] * half.sin(), + half.cos(), + ]; + candidate.transform.rotation = quaternion_product(q, self.base.transform.rotation); + } else { + let delta = (axis_parameter(r, center, self.axis)? - self.initial) * self.side; + if delta == 0.0 { + return Some(self.base.clone()); + } + if self.tool == Tool::Translate { + candidate.transform.translation[self.index] = + self.base.transform.translation[self.index] + delta; + } else { + match &mut candidate.support { + ForceSupport::Sphere { radius, .. } => *radius += delta, + ForceSupport::Box { half_extent, .. } => half_extent[self.index] += delta, + ForceSupport::Global => return None, + } + } + } + Some(candidate) + } +} +#[derive(Default)] +pub struct SceneHandles { + tool: Tool, + drag: Option, + latch: bool, + message: Option<&'static str>, +} +impl SceneHandles { + pub fn busy(&self) -> bool { + self.drag.is_some() || self.latch + } + pub fn controls(&mut self, ui: &mut egui::Ui, scene: &SceneUi, editable: bool) { + if scene.selected_force().is_none() && !self.latch { + return; + } + ui.horizontal_wrapped(|ui| { + for tool in [Tool::Translate,Tool::Rotate,Tool::Support] { + let available=tool!=Tool::Support||scene.selected_force().is_some_and(|o|o.support!=ForceSupport::Global); + let response=ui.add_enabled(editable&&!scene.editing()&&!self.latch&&available, + egui::Button::selectable(self.tool==tool,tool.label()).wrap_mode(egui::TextWrapMode::Extend)); + if response.clicked(){self.tool=tool;} + if !available {response.on_disabled_hover_text("Global support has no boundary to resize. Choose Sphere or Box in properties.");} + } + ui.add(egui::Label::new(if self.tool==Tool::Translate {"World axes · m"}else if self.tool==Tool::Rotate {"Local rotation"}else{"Local size · m"}).wrap_mode(egui::TextWrapMode::Extend)) + .on_hover_text("Drag X/Y/Z. Numeric properties provide keyboard editing. Moving a Global Directional force changes its anchor only; its field remains uniform."); + }); + let text = self.message.unwrap_or("Release applies · Esc cancels"); + let height = ui.text_style_height(&egui::TextStyle::Small) * 2.0; + ui.allocate_ui_with_layout( + egui::vec2(ui.available_width(), height), + egui::Layout::top_down(egui::Align::Min), + |ui| { + ui.add( + egui::Label::new(egui::RichText::new(text).small().color( + if self.message.is_some() { + Color32::YELLOW + } else { + Color32::GRAY + }, + )) + .wrap(), + ); + }, + ); + } + fn cancel(&mut self, scene: &mut SceneUi, down: bool, message: &'static str) { + scene.finish_edit(false); + self.drag = None; + self.latch = down; + self.message = Some(message); + } + /// True consumes this entire frame, including terminal release/cancellation. + pub fn update( + &mut self, + ui: &egui::Ui, + response: &egui::Response, + camera: &OrbitCamera, + side: f64, + scene: &mut SceneUi, + editable: bool, + ) -> bool { + let (down, pressed, released, p, focused, gone) = ui.input(|i| { + ( + i.pointer.button_down(egui::PointerButton::Primary), + i.pointer.button_pressed(egui::PointerButton::Primary), + i.events.iter().any(|e| { + matches!( + e, + egui::Event::PointerButton { + button: egui::PointerButton::Primary, + pressed: false, + .. + } + ) + }), + i.pointer.latest_pos(), + i.focused, + i.events + .iter() + .any(|e| matches!(e, egui::Event::PointerGone)), + ) + }); + if self.latch { + if released { + self.latch = false; + } + return true; + } + if let Some(drag) = &self.drag { + let lost = !editable + || !focused + || (!down && !released) + || response.lost_focus() + || gone + || p.is_none() + || !scene.edit_matches() + || *camera != drag.camera + || response.rect != drag.rect + || side.to_bits() != drag.side.to_bits(); + let escape = ui.input_mut(|i| i.consume_key(i.modifiers, egui::Key::Escape)); + if lost || escape { + self.cancel( + scene, + !released, + if escape { + "Edit cancelled" + } else { + "Edit cancelled · context changed" + }, + ); + return true; + } + let valid = scene.preview_edit(drag.candidate(p.unwrap())); + self.message = if valid { + None + } else { + Some("Invalid preview · release cancels") + }; + if released || !down { + let committed = scene.finish_edit(valid && released); + self.drag = None; + self.message = if committed { + None + } else { + Some("Edit cancelled") + }; + } + return true; + } + if !editable + || !pressed + || !focused + || !response.contains_pointer() + || !ui.input(|i| i.modifiers.is_none()) + { + return false; + } + if scene.display_scene().freeze().is_err() { + return false; + } + let Some(p) = p else { + return false; + }; + let Some(base) = scene.selected_force().cloned() else { + return false; + }; + let Some(index) = hit( + &handles(&base, self.tool, camera, response.rect, side), + self.tool, + p, + camera.orthographic, + ) else { + return false; + }; + response.request_focus(); + if !scene.begin_edit() { + self.latch = down; + self.message = Some("Fix invalid draft before dragging"); + return true; + } + self.drag = Drag::new(base, self.tool, index, camera, response.rect, side, p); + if self.drag.is_none() { + self.cancel( + scene, + down, + "Handle edge-on · change view or use properties", + ); + } else { + self.message = None; + // Native input may coalesce a short press and release in one frame. + // A handle click still owns that frame and preserves the exact base. + if released { + scene.finish_edit(true); + self.drag = None; + } + } + true + } + pub fn paint( + &self, + ui: &egui::Ui, + rect: Rect, + camera: &OrbitCamera, + side: f64, + scene: &SceneUi, + editable: bool, + ) { + if !editable { + return; + } + let display = scene.display_scene(); + let Some(object) = display + .objects + .iter() + .find(|o| Some(o.id) == scene.selected()) + else { + return; + }; + if display.freeze().is_err() { + return; + } + let painter = ui.painter_at(rect); + for h in handles(object, self.tool, camera, rect, side) { + let color = [ + Color32::from_rgb(255, 130, 130), + Color32::from_rgb(145, 230, 145), + Color32::from_rgb(140, 175, 255), + ][h.axis]; + for l in h.lines { + painter.line_segment([l.a.pos, l.b.pos], Stroke::new(2.0, color)); + } + if let Some(p) = h.endpoint { + painter.circle_filled(p, 4.0, color); + let label = ["X", "Y", "Z"][h.axis]; + let galley = + painter.layout_no_wrap(label.into(), egui::FontId::proportional(12.0), color); + let origin = egui::pos2( + (p.x + 7.0).clamp( + rect.left(), + (rect.right() - galley.size().x).max(rect.left()), + ), + (p.y - 7.0 - galley.size().y).clamp( + rect.top(), + (rect.bottom() - galley.size().y).max(rect.top()), + ), + ); + painter.galley(origin, galley, color); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rheon::{ForceKind, ForceScene, ForceTime, ForceTransform, ForceUnits, SceneObjectId}; + fn object() -> ForceObject { + ForceObject { + id: SceneObjectId(1), + name: "Test force".into(), + enabled: true, + transform: ForceTransform { + translation: [0.5; 3], + ..Default::default() + }, + units: ForceUnits::Acceleration, + kind: ForceKind::Directional { vector: [0.0; 3] }, + support: ForceSupport::Sphere { + radius: 0.1, + edge_fraction: 0.2, + }, + time: ForceTime::default(), + } + } + fn scene() -> SceneUi { + let mut s = SceneUi::geometry_fixture( + ForceScene { + version: 1, + objects: vec![object()], + }, + false, + ); + s.select(Some(SceneObjectId(1))); + s + } + fn rect() -> Rect { + Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(600.0, 400.0)) + } + fn screen(camera: &OrbitCamera, rect: Rect, p: V, side: f64) -> Pos2 { + camera + .project(p.map(|v| (v / side) as f32), rect) + .unwrap() + .0 + } + #[test] + fn handles_geometry_ray_translation_units_and_nonidentity_local_rotation() { + let c = OrbitCamera { + yaw: 0.0, + pitch: 0.0, + orthographic: true, + ..Default::default() + }; + let r = rect(); + let mut base = object(); + base.transform.translation[1] = -0.0; + let p = screen(&c, r, [0.86, 0.0, 0.5], 2.0); + let drag = Drag::new(base.clone(), Tool::Translate, 0, &c, r, 2.0, p).unwrap(); + let candidate = drag.candidate(p + egui::vec2(40.0, 0.0)).unwrap(); + let expected = 40.0 * f64::from(c.distance) / f64::from(r.height() * 0.88) * 2.0; + assert!( + (candidate.transform.translation[0] - base.transform.translation[0] - expected).abs() + < 1e-8 + ); + assert_eq!( + candidate.transform.translation.map(f64::to_bits)[1..], + base.transform.translation.map(f64::to_bits)[1..] + ); + assert_eq!(drag.candidate(p).unwrap().transform, base.transform); + let parallel = Ray { + near_parameter: 0.0151, + origin: [0.0; 3], + direction: [1.0, 0.0, 0.0], + }; + assert!(axis_parameter(parallel, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]).is_none()); + assert!(plane_vector(parallel, [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]).is_none()); + let near = Ray { + origin: [0.0; 3], + direction: [0.0, 0.0, 1.0], + near_parameter: 0.0151, + }; + assert!(axis_parameter(near, [0.0, 0.0, 0.005], [1.0, 0.0, 0.0]).is_none()); + assert!(plane_vector(near, [0.0, 0.0, 0.005], [0.0, 0.0, 1.0]).is_none()); + let c = OrbitCamera::default(); + let mut base = object(); + let half = std::f64::consts::FRAC_PI_4; + base.transform.rotation = [0.0, 0.0, half.sin(), half.cos()]; + let start = screen(&c, r, [0.35, 0.5, 0.5], 1.0); + let end = screen(&c, r, [0.5, 0.5, 0.65], 1.0); + let drag = Drag::new(base, Tool::Rotate, 0, &c, r, 1.0, start).unwrap(); + let candidate = drag.candidate(end).unwrap(); + let v = rotate(candidate.transform.rotation, [0.0, 1.0, 0.0]); + assert!( + v[0].abs() < 2e-6 && v[1].abs() < 2e-6 && (v[2] - 1.0).abs() < 2e-6, + "{v:?}" + ); + let mut box_object = object(); + box_object.transform.rotation = [0.0, 0.0, half.sin(), half.cos()]; + box_object.support = ForceSupport::Box { + half_extent: [0.2, 0.3, 0.4], + edge_fraction: 0.2, + }; + let start = screen(&c, r, [0.5, 0.7, 0.5], 1.0); + let end = screen(&c, r, [0.5, 0.75, 0.5], 1.0); + let box_drag = Drag::new(box_object, Tool::Support, 0, &c, r, 1.0, start).unwrap(); + let boxed = box_drag.candidate(end).unwrap(); + if let ForceSupport::Box { half_extent, .. } = boxed.support { + assert!((half_extent[0] - 0.25).abs() < 1e-6); + assert_eq!(half_extent[1..], [0.3, 0.4]); + } else { + panic!("lost box support"); + } + let mut s = scene(); + assert!(s.begin_edit()); + assert!(s.preview_edit(Some(candidate))); + assert!(s.finish_edit(true)); + } + #[test] + fn handles_transaction_invalid_final_position_never_commits_stale_preview() { + let mut s = scene(); + let frozen = s.draft().freeze().unwrap(); + let base = s.draft().clone(); + assert!(s.begin_edit()); + let mut candidate = object(); + candidate.support = ForceSupport::Sphere { + radius: 0.2, + edge_fraction: 0.2, + }; + assert!(s.preview_edit(Some(candidate.clone()))); + assert_eq!(s.draft(), &base); + assert_eq!(s.display_scene().objects[0], candidate); + candidate.support = ForceSupport::Sphere { + radius: -0.2, + edge_fraction: 0.2, + }; + assert!(!s.preview_edit(Some(candidate))); + assert!(!s.finish_edit(true)); + assert_eq!(s.draft(), &base); + assert_eq!(frozen.scene(), &base); + assert!(s.begin_edit()); + let mut candidate = object(); + candidate.transform.translation[1] = 0.75; + assert!(s.preview_edit(Some(candidate))); + s.select(None); + assert_eq!(s.display_scene().as_ref(), &base); + assert!(!s.finish_edit(true)); + assert_eq!(s.draft(), &base); + } + struct Harness { + ctx: egui::Context, + scene: SceneUi, + handles: SceneHandles, + camera: OrbitCamera, + rect: Rect, + consumed: bool, + canvas_size: egui::Vec2, + side: f64, + } + impl Harness { + fn new(tool: Tool) -> Self { + let mut h = Self { + ctx: egui::Context::default(), + scene: scene(), + handles: SceneHandles { + tool, + ..Default::default() + }, + camera: OrbitCamera { + yaw: 0.0, + pitch: 0.0, + orthographic: true, + ..Default::default() + }, + rect: Rect::NOTHING, + consumed: false, + canvas_size: egui::vec2(600.0, 400.0), + side: 1.0, + }; + h.frame(vec![], true, true, egui::Modifiers::NONE); + h.frame(vec![], true, true, egui::Modifiers::NONE); + h + } + fn frame( + &mut self, + events: Vec, + focused: bool, + editable: bool, + modifiers: egui::Modifiers, + ) { + let _ = self.ctx.run( + egui::RawInput { + screen_rect: Some(Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(700.0, 500.0), + )), + events, + focused, + modifiers, + ..Default::default() + }, + |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let (rect, response) = + ui.allocate_exact_size(self.canvas_size, egui::Sense::click_and_drag()); + self.rect = rect; + self.consumed = self.handles.update( + ui, + &response, + &self.camera, + self.side, + &mut self.scene, + editable, + ); + self.handles.paint( + ui, + rect, + &self.camera, + self.side, + &self.scene, + editable, + ); + }); + }, + ); + } + fn point(&self, x: f64) -> Pos2 { + screen(&self.camera, self.rect, [x, 0.5, 0.5], self.side) + } + fn button(&mut self, p: Pos2, pressed: bool, modifiers: egui::Modifiers) { + self.frame( + vec![ + egui::Event::PointerMoved(p), + egui::Event::PointerButton { + pos: p, + button: egui::PointerButton::Primary, + pressed, + modifiers, + }, + ], + true, + true, + modifiers, + ); + } + fn press(&mut self) -> Pos2 { + let x = if self.handles.tool == Tool::Support { + 0.6 + } else { + 0.68 + }; + let p = self.point(x); + self.frame( + vec![egui::Event::PointerMoved(p)], + true, + true, + egui::Modifiers::NONE, + ); + self.button(p, true, egui::Modifiers::NONE); + assert!(self.consumed && self.scene.editing()); + p + } + } + #[test] + fn handles_pointer_release_commits_once_and_escape_consumes_until_release() { + let mut short = Harness::new(Tool::Translate); + let p = short.point(0.68); + let base = short.scene.draft().clone(); + short.frame( + vec![ + egui::Event::PointerMoved(p), + egui::Event::PointerButton { + pos: p, + button: egui::PointerButton::Primary, + pressed: true, + modifiers: egui::Modifiers::NONE, + }, + egui::Event::PointerButton { + pos: p, + button: egui::PointerButton::Primary, + pressed: false, + modifiers: egui::Modifiers::NONE, + }, + ], + true, + true, + egui::Modifiers::NONE, + ); + assert!(short.consumed && !short.handles.busy() && !short.scene.editing()); + assert_eq!(short.scene.draft(), &base); + let mut h = Harness::new(Tool::Translate); + let p = h.press(); + let base = h.scene.draft().clone(); + let end = p + egui::vec2(30.0, 0.0); + h.frame( + vec![egui::Event::PointerMoved(end)], + true, + true, + egui::Modifiers::NONE, + ); + assert!(h.consumed); + assert_eq!(h.scene.draft(), &base); + h.button(end, false, egui::Modifiers::NONE); + assert!(h.consumed && !h.scene.editing()); + assert!(h.scene.draft().objects[0].transform.translation[0] > 0.5); + assert_eq!(h.scene.selected(), Some(SceneObjectId(1))); + h.frame(vec![], true, true, egui::Modifiers::NONE); + assert!(!h.consumed); + let mut h = Harness::new(Tool::Translate); + let p = h.press(); + let base = h.scene.draft().clone(); + h.frame( + vec![ + egui::Event::PointerMoved(p + egui::vec2(20.0, 0.0)), + egui::Event::Key { + key: egui::Key::Escape, + physical_key: None, + pressed: true, + repeat: false, + modifiers: egui::Modifiers::NONE, + }, + ], + true, + true, + egui::Modifiers::NONE, + ); + assert!(h.consumed && !h.scene.editing() && h.handles.latch); + assert_eq!(h.scene.draft(), &base); + h.frame( + vec![egui::Event::PointerMoved(p + egui::vec2(50.0, 0.0))], + true, + true, + egui::Modifiers::NONE, + ); + assert!(h.consumed); + h.button(p, false, egui::Modifiers::NONE); + assert!(h.consumed && !h.handles.latch); + assert_eq!(h.scene.draft(), &base); + } + #[test] + fn handles_pointer_invalid_support_release_restores_base_and_snapshot() { + let mut h = Harness::new(Tool::Support); + let frozen = h.scene.draft().freeze().unwrap(); + h.press(); + let valid = h.point(0.7); + h.frame( + vec![egui::Event::PointerMoved(valid)], + true, + true, + egui::Modifiers::NONE, + ); + assert!(h.scene.editing()); + let invalid = h.point(0.3); + h.frame( + vec![egui::Event::PointerMoved(invalid)], + true, + true, + egui::Modifiers::NONE, + ); + assert_eq!(h.handles.message, Some("Invalid preview · release cancels")); + h.button(invalid, false, egui::Modifiers::NONE); + assert!(h.consumed && !h.scene.editing()); + assert_eq!(h.scene.draft(), frozen.scene()); + } + #[test] + fn handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off() { + for reason in 0..8 { + let mut h = Harness::new(Tool::Translate); + let p = h.press(); + let base = h.scene.draft().clone(); + let mut events = vec![]; + let mut focused = true; + let mut editable = true; + match reason { + 0 => focused = false, + 1 => events.push(egui::Event::PointerGone), + 2 => h.scene.select(None), + 3 => h.camera.yaw += 0.2, + 4 => editable = false, + 5 => h.scene.begin_run(false), + 6 => h.canvas_size.x -= 20.0, + _ => h.side = 2.0, + } + h.frame(events, focused, editable, egui::Modifiers::NONE); + assert!(h.consumed && !h.scene.editing()); + assert_eq!(h.scene.draft(), &base); + h.button(p, false, egui::Modifiers::NONE); + assert!(h.consumed); + assert_eq!(h.scene.draft(), &base); + } + let mut outside = Harness::new(Tool::Translate); + outside.press(); + let p = egui::pos2(outside.rect.right() + 50.0, outside.rect.center().y); + outside.frame( + vec![egui::Event::PointerMoved(p)], + true, + true, + egui::Modifiers::NONE, + ); + assert!(outside.consumed && outside.scene.editing()); + outside.button(p, false, egui::Modifiers::NONE); + assert!(outside.consumed && !outside.scene.editing()); + assert!(outside.scene.draft().objects[0].transform.translation[0] > 1.0); + let mut middle = Harness::new(Tool::Translate); + let p = middle.point(0.68); + middle.frame( + vec![ + egui::Event::PointerMoved(p), + egui::Event::PointerButton { + pos: p, + button: egui::PointerButton::Middle, + pressed: true, + modifiers: egui::Modifiers::NONE, + }, + ], + true, + true, + egui::Modifiers::NONE, + ); + assert!(!middle.consumed && !middle.scene.editing()); + let mut edge = Harness::new(Tool::Translate); + let p = edge.point(0.5); + edge.button(p, true, egui::Modifiers::NONE); + assert!(edge.consumed && edge.handles.latch && !edge.scene.editing()); + assert_eq!( + edge.handles.message, + Some("Handle edge-on · change view or use properties") + ); + edge.button(p, false, egui::Modifiers::NONE); + assert!(edge.consumed && !edge.handles.latch); + let mut h = Harness::new(Tool::Translate); + let p = h.point(0.68); + h.button(p, true, egui::Modifiers::SHIFT); + assert!(!h.consumed && !h.scene.editing()); + h.button(p, false, egui::Modifiers::SHIFT); + } + #[test] + fn handles_named_controls_fit_narrow_without_touching_authoring_values() { + for width in [220.0, 480.0] { + let ctx = egui::Context::default(); + let s = scene(); + let base = s.draft().clone(); + let mut handles = SceneHandles::default(); + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width + 16.0, 500.0), + )), + ..Default::default() + }, + |ctx| { + egui::CentralPanel::default().show(ctx, |ui| { + let response = ui.scope(|ui| handles.controls(ui, &s, true)); + assert!( + response.response.rect.height() < 100.0, + "{:?}", + response.response.rect + ); + }); + }, + ); + assert_eq!(s.draft(), &base); + assert!(!s.editing()); + } + } +} diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index 83b4c4b8..a0b5b692 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -31,7 +31,13 @@ impl NewForce { } } } +struct DraftEdit { + base: ForceObject, + preview: ForceObject, + valid: bool, +} pub struct SceneUi { + edit: Option, forces: ForceScene, selected: Option, next_id: Option, @@ -44,6 +50,7 @@ pub struct SceneUi { impl Default for SceneUi { fn default() -> Self { Self { + edit: None, forces: ForceScene::default(), selected: None, next_id: Some(1), @@ -68,6 +75,107 @@ impl SceneUi { pub fn draft(&self) -> &ForceScene { &self.forces } + pub fn selected_force(&self) -> Option<&ForceObject> { + (self.context == SceneContext::Draft) + .then(|| { + self.forces + .objects + .iter() + .find(|o| Some(o.id) == self.selected) + }) + .flatten() + } + pub fn editing(&self) -> bool { + self.edit.is_some() + } + pub fn display_scene(&self) -> std::borrow::Cow<'_, ForceScene> { + if let Some(edit) = &self.edit + && self.edit_matches() + { + let mut scene = self.forces.clone(); + if let Some(object) = scene.objects.iter_mut().find(|o| o.id == edit.base.id) { + *object = edit.preview.clone(); + } + std::borrow::Cow::Owned(scene) + } else { + std::borrow::Cow::Borrowed(&self.forces) + } + } + pub fn begin_edit(&mut self) -> bool { + if self.edit.is_some() || self.forces.freeze().is_err() { + return false; + } + let Some(base) = self.selected_force().cloned() else { + return false; + }; + self.edit = Some(DraftEdit { + preview: base.clone(), + base, + valid: true, + }); + true + } + pub fn edit_matches(&self) -> bool { + self.edit.as_ref().is_some_and(|edit| { + self.selected_force().is_some_and(|current| { + current == &edit.base + && serde_json::to_vec(current) + .ok() + .is_some_and(|bytes| serde_json::to_vec(&edit.base).ok() == Some(bytes)) + }) + }) + } + pub fn preview_edit(&mut self, candidate: Option) -> bool { + let Some(edit) = &self.edit else { + return false; + }; + let candidate = candidate.filter(|c| c.id == edit.base.id); + let valid = candidate.as_ref().is_some_and(|candidate| { + let mut scene = self.forces.clone(); + let Some(object) = scene.objects.iter_mut().find(|o| o.id == candidate.id) else { + return false; + }; + *object = candidate.clone(); + scene.freeze().is_ok() + }); + let edit = self.edit.as_mut().unwrap(); + edit.valid = valid; + if valid { + edit.preview = candidate.unwrap(); + } + valid + } + pub fn finish_edit(&mut self, apply: bool) -> bool { + let matches = self.edit_matches(); + let Some(edit) = self.edit.take() else { + return false; + }; + if apply && matches && edit.valid { + let mut candidate_scene = self.forces.clone(); + let Some(candidate) = candidate_scene + .objects + .iter_mut() + .find(|o| o.id == edit.base.id) + else { + return false; + }; + *candidate = edit.preview.clone(); + if candidate_scene.freeze().is_err() { + return false; + } + if let Some(object) = self + .forces + .objects + .iter_mut() + .find(|o| o.id == edit.base.id) + { + *object = edit.preview; + self.validate(); + return true; + } + } + false + } pub fn selected(&self) -> Option { self.selected } @@ -191,7 +299,7 @@ impl SceneUi { ui.add_enabled_ui(has_run,|ui|{ui.selectable_value(&mut self.context,SceneContext::Retained,"Retained run");}); }); if previous!=self.context {self.selected=None;} - let editable=self.context==SceneContext::Draft && !active_worker; + let editable=self.context==SceneContext::Draft && !active_worker && !self.editing(); if self.context==SceneContext::Draft && !self.forces.objects.is_empty() { ui.colored_label(egui::Color32::YELLOW,"Authored-force runs unavailable") .on_hover_text("Runtime and archive integration is pending. No authored force will be silently ignored."); @@ -219,10 +327,14 @@ impl SceneUi { } else {ui.label("Authored-force metadata unavailable").on_hover_text("This legacy retained run has no canonical authored-force document. Its physics is not inferred from the current draft.");} ui.separator(); ui.heading("Properties"); + if self.editing() {ui.small("Drag preview · release applies · Esc cancels");} if self.selected.is_some() && self.selected==self.model_id() { if let Some(model)=model {ui.label(format!("{} triangles",model.triangles));ui.label(format!("Bounds {:?} … {:?} m",model.bounds.0,model.bounds.1));ui.small("Accepted geometry · placement uses Import / apply mesh in setup.");} } else if self.context==SceneContext::Draft { - if let Some(object)=self.forces.objects.iter_mut().find(|o|Some(o.id)==self.selected) { + if self.edit_matches() { + let mut candidate=self.edit.as_ref().unwrap().preview.clone(); + ui.add_enabled_ui(false,|ui|force_properties(ui,&mut candidate)); + } else if let Some(object)=self.forces.objects.iter_mut().find(|o|Some(o.id)==self.selected) { let changed=ui.push_id(("force-properties",object.id.0), |ui| ui.add_enabled_ui(editable,|ui|force_properties(ui,object)).inner).inner; if changed {self.validate();} if ui.add_enabled(editable,egui::Button::new("Remove force")).clicked(){self.remove_selected();} @@ -605,6 +717,34 @@ mod tests { scene.forces.objects[0].transform.rotation = quaternion(angles); assert!(scene.draft().freeze().is_ok()); } + // Revalidate the entire current containing document on release, not only + // the earlier candidate. No solver or recording owner is involved. + let mut s = SceneUi::default(); + let id = s.add(NewForce::Directional).unwrap(); + s.forces.objects[0].transform.translation[1] = -0.0; + let base = s.forces.objects[0].clone(); + assert!(s.begin_edit()); + let mut candidate = base.clone(); + candidate.transform.translation[0] = 0.25; + assert!(s.preview_edit(Some(candidate))); + s.add(NewForce::Radial).unwrap(); + s.select(Some(id)); + s.forces.objects[1].support = ForceSupport::Sphere { + radius: 0.0, + edge_fraction: 0.0, + }; + assert!(!s.finish_edit(true)); + assert_eq!( + s.forces.objects[0].transform.translation.map(f64::to_bits), + base.transform.translation.map(f64::to_bits) + ); + s.forces.objects[1].support = ForceSupport::Global; + assert!(s.begin_edit()); + s.forces.objects[0].transform.translation[1] = 0.0; + assert!( + !s.finish_edit(true), + "signed-zero base conflict must not commit" + ); } #[test] fn scene_force_refusal_projects_disabled_run_without_dispatch() { diff --git a/src/bin/desktop/scene_view.rs b/src/bin/desktop/scene_view.rs index 9d7f59aa..ced3a51a 100644 --- a/src/bin/desktop/scene_view.rs +++ b/src/bin/desktop/scene_view.rs @@ -18,14 +18,14 @@ pub struct MeshRef<'a> { pub triangles: &'a [[usize; 3]], } #[derive(Clone, Copy, Debug)] -struct Point { - pos: Pos2, - depth: f32, +pub(super) struct Point { + pub pos: Pos2, + pub depth: f32, } #[derive(Clone, Copy, Debug)] -struct Line { - a: Point, - b: Point, +pub(super) struct Line { + pub a: Point, + pub b: Point, } struct Object { id: SceneObjectId, @@ -68,7 +68,7 @@ fn cross(a: V3, b: V3) -> V3 { fn lerp(a: V3, b: V3, t: f32) -> V3 { std::array::from_fn(|d| a[d] + (b[d] - a[d]) * t) } -fn normalized(p: [f64; 3], side: f64) -> Option { +pub(super) fn normalized(p: [f64; 3], side: f64) -> Option { let p = p.map(|v| v / side); (side.is_finite() && side > 0.0 && p.iter().all(|v| v.is_finite() && v.abs() <= 1.0e12)) .then(|| p.map(|v| v as f32)) @@ -131,7 +131,7 @@ fn clip_line(line: Line, rect: Rect, ortho: bool) -> Option { b: point(upper), }) } -fn segment(camera: &OrbitCamera, rect: Rect, mut a: V3, mut b: V3) -> Option { +pub(super) fn segment(camera: &OrbitCamera, rect: Rect, mut a: V3, mut b: V3) -> Option { let da = depth(camera, a); let db = depth(camera, b); if !da.is_finite() || !db.is_finite() || (da < NEAR && db < NEAR) { @@ -179,7 +179,7 @@ fn clipped_triangle(camera: &OrbitCamera, rect: Rect, triangle: [V3; 3]) -> Vec< .map(|i| [p[0], p[i], p[i + 1]]) .collect() } -fn line_hit(line: Line, p: Pos2, ortho: bool) -> Option<(f32, f32)> { +pub(super) fn line_hit(line: Line, p: Pos2, ortho: bool) -> Option<(f32, f32)> { let vector = line.b.pos - line.a.pos; let length = vector.length_sq(); let t = if length > 0.0001 { @@ -217,7 +217,7 @@ fn triangle_hit(triangle: [Point; 3], p: Pos2, ortho: bool) -> Option { }; (z.is_finite() && z > 0.0).then_some(z as f32) } -fn rotate([x, y, z, w]: [f64; 4], [a, b, c]: [f64; 3]) -> [f64; 3] { +pub(super) fn rotate([x, y, z, w]: [f64; 4], [a, b, c]: [f64; 3]) -> [f64; 3] { [ (1.0 - 2.0 * (y * y + z * z)) * a + 2.0 * (x * y - z * w) * b + 2.0 * (x * z + y * w) * c, 2.0 * (x * y + z * w) * a + (1.0 - 2.0 * (x * x + z * z)) * b + 2.0 * (y * z - x * w) * c, @@ -363,12 +363,13 @@ impl Projection { result.pick_incomplete = true; return result; } + let display_scene = state.display_scene(); if state.context == SceneContext::Draft { - if state.draft().freeze().is_err() { + if display_scene.freeze().is_err() { result.warning = Some("Invalid force draft · fix properties to preview"); result.pick_incomplete = true; } else { - for object in &state.draft().objects { + for object in &display_scene.objects { let anchor_position = normalized(object.transform.translation, side); let mut unavailable = anchor_position.is_none(); let anchor = anchor_position.and_then(|p| project(camera, p, rect)); diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index c4d62def..d4311bd4 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -61,6 +61,7 @@ fn view_mode_tabs(ui: &mut egui::Ui, current: &mut ViewMode, blocked: bool) -> [ }) } pub struct Viewport { + scene_handles: super::scene_handles::SceneHandles, /// Draft authored fields have no runtime/preview integration yet. pub authored_forces_pending: bool, pub collision_mesh: Option>, @@ -82,6 +83,7 @@ pub struct Viewport { impl Default for Viewport { fn default() -> Self { Self { + scene_handles: Default::default(), authored_forces_pending: false, camera: OrbitCamera::default(), engineering: None, @@ -111,6 +113,7 @@ pub struct ViewInputs<'a> { pub grid_edge: usize, pub inspector: Option<&'a super::inspector::Inspector>, pub scene: Option<&'a mut super::scene_ui::SceneUi>, + pub editable_scene: bool, } impl Viewport { #[cfg(test)] @@ -132,6 +135,7 @@ impl Viewport { grid_edge, inspector, scene: None, + editable_scene: false, }, ); } @@ -143,12 +147,17 @@ impl Viewport { grid_edge, inspector, mut scene, + editable_scene, } = inputs; - if self.authored_forces_pending { + if self.authored_forces_pending || self.scene_handles.busy() { self.mode = ViewMode::Appearance; } ui.horizontal_wrapped(|ui| { - view_mode_tabs(ui, &mut self.mode, self.authored_forces_pending); + view_mode_tabs( + ui, + &mut self.mode, + self.authored_forces_pending || self.scene_handles.busy(), + ); if self.mode != ViewMode::Appearance { ui.checkbox(&mut self.engineering_camera.orthographic, "Orthographic"); if ui.button("Frame view").clicked() { @@ -159,6 +168,11 @@ impl Viewport { } } }); + if self.mode == ViewMode::Appearance + && let Some(scene) = scene.as_deref() + { + self.scene_handles.controls(ui, scene, editable_scene); + } match self.mode { ViewMode::Obstacle => self .engineering @@ -209,8 +223,20 @@ impl Viewport { std::mem::swap(&mut self.camera, &mut self.engineering_camera); return; } - self.input(ui, &response, rect, 2.35); - if let Some(scene) = scene.as_deref_mut() { + let consumed = scene.as_deref_mut().is_some_and(|scene| { + self.scene_handles.update( + ui, + &response, + &self.camera, + self.domain_side, + scene, + editable_scene, + ) + }); + if !consumed { + self.input(ui, &response, rect, 2.35); + } + if !consumed && let Some(scene) = scene.as_deref_mut() { let mesh = self .collision_mesh .as_ref() @@ -511,6 +537,14 @@ impl Viewport { mesh, scene, ); + self.scene_handles.paint( + ui, + rect, + &self.camera, + self.domain_side, + scene, + editable_scene, + ); } } fn input(&mut self, ui: &egui::Ui, response: &egui::Response, rect: Rect, frame_distance: f32) { @@ -559,7 +593,9 @@ impl Viewport { _ => {} } } - if input.key_pressed(egui::Key::Home) || input.key_pressed(egui::Key::F) { + if (response.has_focus() || !ui.ctx().wants_keyboard_input()) + && (input.key_pressed(egui::Key::Home) || input.key_pressed(egui::Key::F)) + { self.camera = OrbitCamera { distance: frame_distance, ..Default::default() diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index a5ca85df..ce04a4ef 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -52,6 +52,8 @@ pub mod run_sets; pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; +#[path = "desktop/scene_handles.rs"] +mod scene_handles; #[path = "desktop/scene_ui.rs"] mod scene_ui; #[path = "desktop/scene_view.rs"] @@ -2524,6 +2526,7 @@ impl eframe::App for App { .filter(|_| retained) .map(|h| &h.inspector), scene: Some(&mut self.scene), + editable_scene: self.worker.is_none(), }, ) }); From 8148fa239a60502a1245d5a69d0eeb8ceb905c16 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 20 Oct 2026 23:47:58 +0000 Subject: [PATCH 13/25] Add isolated workerless native playback presentation harness --- .../.gitattributes | 1 + .../guarded-standalone-binary-seal.json | 9 + .../manifest.json | 68 + .../rheon-ui-presentation-check2.log | 2 + .../rheon-ui-presentation-clippy2.log | 2 + .../rheon-ui-presentation-diff2.log | 0 .../rheon-ui-presentation-exact2.log | 8 + .../rheon-ui-presentation-fmt2.log | 0 .../rheon-ui-presentation-list1.log | 280 +++ .../rheon-ui-presentation-list4.log | 195 ++ ...heon-ui-presentation-production-build2.log | 2 + ...heon-ui-presentation-standalone-build2.log | 1750 +++++++++++++++++ docs/plans/ui-refinement/execution-ledger.md | 38 + docs/plans/ui-refinement/plan.md | 15 +- .../native-synthetic-presentation-handoff.md | 58 + .../runtime-force-integration-admission.md | 53 + ...ynthetic-retained-presentation-contract.md | 56 + ...hetic-retained-presentation-plan-review.md | 140 ++ ...tic-retained-presentation-source-review.md | 65 + .../desktop/native_presentation_harness.rs | 308 +++ src/bin/desktop/viewport.rs | 18 + src/bin/rheon-desktop.rs | 9 + 22 files changed, 3075 insertions(+), 2 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/guarded-standalone-binary-seal.json create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-check2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-clippy2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-diff2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-exact2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-fmt2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list1.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list4.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-production-build2.log create mode 100644 docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-standalone-build2.log create mode 100644 docs/plans/ui-refinement/reports/native-synthetic-presentation-handoff.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-integration-admission.md create mode 100644 docs/plans/ui-refinement/reports/synthetic-retained-presentation-contract.md create mode 100644 docs/plans/ui-refinement/reports/synthetic-retained-presentation-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/synthetic-retained-presentation-source-review.md create mode 100644 src/bin/desktop/native_presentation_harness.rs diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/.gitattributes b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/guarded-standalone-binary-seal.json b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/guarded-standalone-binary-seal.json new file mode 100644 index 00000000..8a92f3a7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/guarded-standalone-binary-seal.json @@ -0,0 +1,9 @@ +{ + "artifact": "/workspace/scratch/rheon-ui-presentation/rheon-ui-synthetic-presentation-testcfg-guarded-425a986-increment.bin", + "bytes": 235185520, + "sha256": "040715719f4e34e04951c8ad428024cbdbd219d7b65ac95d64d54e3e6e8d5cbd", + "base": "425a9869fa3f36b7e3133ddbd213a493d7b5bab3", + "build_cfg": "test", + "rustc_test_harness": false, + "executed": false +} diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/manifest.json b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/manifest.json new file mode 100644 index 00000000..8e94f7e5 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/manifest.json @@ -0,0 +1,68 @@ +{ + "parent": "425a9869fa3f36b7e3133ddbd213a493d7b5bab3", + "source_sha256": { + "src/bin/desktop/native_presentation_harness.rs": "7ecb3487c24d57eb726e8212d1393bf855ca13125af53021bb0c6237dc056c6e", + "src/bin/desktop/viewport.rs": "c79ef51daf9f0079f424f9d6d24343931d70d2f5d4fe233ee978931080aaa462", + "src/bin/rheon-desktop.rs": "daf9b7e54d89decfbfbff8ae8dd7cb17cc4fbab3fd50f3323f442f02dd1c55b3" + }, + "exact_test": "native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless", + "native_entry_is_test": false, + "native_executed": false, + "standalone_build": "cargo rustc --locked --offline --features desktop --bin rheon-desktop -- --cfg test", + "binary": { + "artifact": "/workspace/scratch/rheon-ui-presentation/rheon-ui-synthetic-presentation-testcfg-guarded-425a986-increment.bin", + "bytes": 235185520, + "sha256": "040715719f4e34e04951c8ad428024cbdbd219d7b65ac95d64d54e3e6e8d5cbd", + "base": "425a9869fa3f36b7e3133ddbd213a493d7b5bab3", + "build_cfg": "test", + "rustc_test_harness": false, + "executed": false + }, + "logs": { + "rheon-ui-presentation-list4.log": { + "sha256": "1574f9ddcd4c335baf5bb4fa7b5883cc63a4b6d201a6990679590a5dd8016354", + "bytes": 20088, + "source": "/tmp/rheon-ui-presentation-list4.log" + }, + "rheon-ui-presentation-exact2.log": { + "sha256": "e35f03ce95e98aa91a03942256c60f0f6a9d1172b1265ca6a462fbc9e032885a", + "bytes": 421, + "source": "/tmp/rheon-ui-presentation-exact2.log" + }, + "rheon-ui-presentation-standalone-build2.log": { + "sha256": "45a096a0556c6f9712346da4e1c8b77e2ba19b7dcea77d5196aa6765bd286658", + "bytes": 55319, + "source": "/tmp/rheon-ui-presentation-standalone-build2.log" + }, + "rheon-ui-presentation-production-build2.log": { + "sha256": "4f144ddc9d69c6d17c7001e9fca8e6d1a70839f4c43a917e436cd3162792c40e", + "bytes": 131, + "source": "/tmp/rheon-ui-presentation-production-build2.log" + }, + "rheon-ui-presentation-clippy2.log": { + "sha256": "3856eab6f80feb9a95243e537de6199854ef0a153049ab2b61af84441cb51bc9", + "bytes": 130, + "source": "/tmp/rheon-ui-presentation-clippy2.log" + }, + "rheon-ui-presentation-check2.log": { + "sha256": "b8dd291b69b62c0719c18a5c4ae02c0c692d7e36311fa3a586fa4e4b92662e11", + "bytes": 130, + "source": "/tmp/rheon-ui-presentation-check2.log" + }, + "rheon-ui-presentation-fmt2.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-presentation-fmt2.log" + }, + "rheon-ui-presentation-diff2.log": { + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "bytes": 0, + "source": "/tmp/rheon-ui-presentation-diff2.log" + }, + "rheon-ui-presentation-list1.log": { + "sha256": "cbb880f9255700c467f820fdfb0fc6856b3410ce5549fb5d4a90a20b44e313b3", + "bytes": 10297, + "source": "/tmp/rheon-ui-presentation-list1.log" + } + } +} diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-check2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-check2.log new file mode 100644 index 00000000..ab8d7678 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-check2.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-clippy2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-clippy2.log new file mode 100644 index 00000000..5e775e24 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-clippy2.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.81s diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-diff2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-diff2.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-exact2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-exact2.log new file mode 100644 index 00000000..5b456096 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-exact2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 189 filtered out; finished in 0.06s + diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-fmt2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-fmt2.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list1.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list1.log new file mode 100644 index 00000000..19485ee8 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list1.log @@ -0,0 +1,280 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error[E0433]: failed to resolve: could not find `egui_winit` in `egui_glow` + --> src/bin/desktop/native_presentation_harness.rs:291:28 + | +291 | use eframe::egui_glow::egui_winit::winit::platform::x11::EventLoopBuilderExtX11; + | ^^^^^^^^^^ could not find `egui_winit` in `egui_glow` + +error[E0599]: no method named `with_any_thread` found for mutable reference `&mut EventLoopBuilder` in the current scope + --> src/bin/desktop/native_presentation_harness.rs:304:25 + | +304 | builder.with_any_thread(true); + | ^^^^^^^^^^^^^^^ method not found in `&mut EventLoopBuilder` + | + = help: items from traits can only be used if the trait is in scope +help: the following traits which provide `with_any_thread` are implemented but not in scope; perhaps you want to import one of them + | + 2 + use winit::platform::wayland::EventLoopBuilderExtWayland; + | + 2 + use winit::platform::x11::EventLoopBuilderExtX11; + | + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `scene_ui::SceneUi`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::option::Option`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `archive_view::ArchiveView`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `RunSetView`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `configured_controls::ConfiguredControls`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `saved_comparison::SavedComparisonPage`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `comparison_field_view::ComparisonFieldView`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `collision_ui::CollisionControls`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::string::String`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `settings::MaterialControls`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `emitter_controls::EmitterControls`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::option::Option`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::option::Option>`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `std::vec::Vec`, which does not implement the `Copy` trait + +error[E0509]: cannot move out of type `App`, which implements the `Drop` trait + --> src/bin/desktop/native_presentation_harness.rs:73:19 + | +73 | let mut app = App { + | ___________________^ +74 | | history: Some(history), +75 | | running_request: Some(request), +76 | | follow_live: false, +... | +82 | | ..Default::default() +83 | | }; + | | ^ + | | | + | |_____cannot move out of here + | move occurs because value has type `viewport::Viewport`, which does not implement the `Copy` trait + +Some errors have detailed explanations: E0433, E0509, E0599. +For more information about an error, try `rustc --explain E0433`. +error: could not compile `rheon` (bin "rheon-desktop" test) due to 18 previous errors diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list4.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list4.log new file mode 100644 index 00000000..553e1a40 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-list4.log @@ -0,0 +1,195 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 7.94s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +190 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-production-build2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-production-build2.log new file mode 100644 index 00000000..81f8de05 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-production-build2.log @@ -0,0 +1,2 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 15.73s diff --git a/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-standalone-build2.log b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-standalone-build2.log new file mode 100644 index 00000000..8204329f --- /dev/null +++ b/docs/plans/ui-refinement/evidence/synthetic-presentation-20261020/rheon-ui-presentation-standalone-build2.log @@ -0,0 +1,1750 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) +warning: unused import: `Sha256` + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:10:20 + | +10 | use sha2::{Digest, Sha256}; + | ^^^^^^ + | + = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + +warning: unused imports: `Duration` and `Instant` + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:13:12 + | +13 | time::{Duration, Instant}, + | ^^^^^^^^ ^^^^^^^ + +warning: unused import: `Sha256` + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:10:20 + | +10 | use sha2::{Digest, Sha256}; + | ^^^^^^ + +warning: unused imports: `Duration`, `Instant`, and `thread` + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:13:5 + | +13 | thread, + | ^^^^^^ +14 | time::{Duration, Instant}, + | ^^^^^^^^ ^^^^^^^ + +warning: unused imports: `ModelProcess` and `scene::Scene` + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:5:32 + | +5 | model_process::{ChildSlot, ModelProcess}, + | ^^^^^^^^^^^^ +6 | scene::Scene, + | ^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/mod.rs:4:1 + | +4 | pub(crate) mod cpu_contract_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `Duration` and `atomic::AtomicBool` + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:14:24 + | +14 | sync::{Arc, Mutex, atomic::AtomicBool}, + | ^^^^^^^^^^^^^^^^^^ +15 | time::{Duration, SystemTime, UNIX_EPOCH}, + | ^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/mod.rs:4:1 + | + 4 | pub(crate) mod cpu_contract_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `Instant` + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:31:22 + | + 31 | time::{Duration, Instant}, + | ^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:366:1 + | +366 | mod accepted_provider_contract_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `RecordedPressureSelection`, `RecordedProviderRejection`, and `verify_configured_recording` + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:9:36 + | + 9 | RecordedConfiguredSession, RecordedPressureSelection, RecordedProviderRejection, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ + 10 | configured_publication, configured_run, verify_configured_recording, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:797:1 + | +797 | mod accepted_recording_contract_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `sync::atomic::Ordering` + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:13:41 + | + 13 | use std::{path::Path, process::Command, sync::atomic::Ordering}; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:797:1 + | +797 | mod accepted_recording_contract_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:687:9 + | +687 | use super::*; + | ^^^^^^^^ + +warning: unused import: `engine::ExecutionPolicy` + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:4:64 + | +4 | cpu_contract_tests::Fixture, diagnostic_runs::MemberState, engine::ExecutionPolicy, + | ^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `Digest` and `Sha256` + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:6:12 + | +6 | use sha2::{Digest, Sha256}; + | ^^^^^^ ^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/ai_comparison/engine.rs:809:9 + | +809 | use super::*; + | ^^^^^^^^ + +warning: unused imports: `DiagnosticReader`, `DiagnosticRecorder`, `DiagnosticTerminal`, `RecordingLimits`, and `TerminalOutcome` + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:3:5 + | + 3 | RecordingLimits, + | ^^^^^^^^^^^^^^^ + 4 | diagnostic::{DiagnosticReader, DiagnosticRecorder, DiagnosticTerminal, TerminalOutcome}, + | ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/engine.rs:900:1 + | +900 | mod typed_tests; + | ^^^^^^^^^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/ai_comparison/model_process.rs:1038:9 + | +1038 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/ai_comparison/scene.rs:123:9 + | +123 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/ai_comparison/view.rs:221:9 + | +221 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::super::cpu_contract_tests::assert_child_liveness` + --> src/bin/desktop/ai_comparison/worker.rs:352:9 + | +352 | use super::super::cpu_contract_tests::assert_child_liveness; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `Event`, `Modifiers`, `PointerButton`, `Pos2`, `RawInput`, `Rect`, and `Vec2` + --> src/bin/desktop/ai_comparison/mod.rs:427:24 + | +427 | use eframe::egui::{Event, Modifiers, PointerButton, Pos2, RawInput, Rect, Vec2}; + | ^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^ ^^^^^^^^ ^^^^ ^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/ai_comparison/mod.rs:425:1 + | +425 | mod saved_rerun_tests { + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/carrier_execution.rs:176:9 + | +176 | use super::*; + | ^^^^^^^^ + +warning: unused import: `member` + --> src/bin/desktop/configured_comparison_tests.rs:3:67 + | + 3 | use super::configured_worker_tests::{Output, app, finish, launch, member, package, set}; + | ^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:15:1 + | +15 | mod configured_comparison_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `*`, `diagnostic::NoCandidateReason`, and `runtime::*` + --> src/bin/desktop/configured_comparison_tests.rs:13:9 + | +13 | diagnostic::NoCandidateReason, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +14 | provider::{runtime::*, *}, + | ^^^^^^^^^^ ^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:15:1 + | +15 | mod configured_comparison_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `process::Command` + --> src/bin/desktop/configured_comparison_tests.rs:21:5 + | +21 | process::Command, + | ^^^^^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:15:1 + | +15 | mod configured_comparison_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `crate::run_sets::NativePressureMethod` + --> src/bin/desktop/configured_controls.rs:519:9 + | +519 | use crate::run_sets::NativePressureMethod; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `rheon::SnapshotStage` + --> src/bin/desktop/configured_controls.rs:520:9 + | +520 | use rheon::SnapshotStage; + | ^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `process::Command` + --> src/bin/desktop/configured_producer_exit_tests.rs:24:5 + | +24 | process::Command, + | ^^^^^^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:20:1 + | +20 | mod configured_producer_exit_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused imports: `*` and `runtime::*` + --> src/bin/desktop/configured_worker_tests.rs:6:20 + | + 6 | provider::{runtime::*, *}, + | ^^^^^^^^^^ ^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:23:1 + | +23 | mod configured_worker_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `sync::Mutex` + --> src/bin/desktop/configured_worker_tests.rs:17:5 + | +17 | sync::Mutex, + | ^^^^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/rheon-desktop.rs:23:1 + | +23 | mod configured_worker_tests; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/emitter_controls.rs:147:9 + | +147 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/engineering.rs:549:9 + | +549 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/export_ui.rs:57:9 + | +57 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/archive_view.rs:69:9 + | +69 | use super::*; + | ^^^^^^^^ + +warning: unused import: `rheon::*` + --> src/bin/desktop/archive_view.rs:70:9 + | +70 | use rheon::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/collision_ui.rs:117:9 + | +117 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/comparison_field_view.rs:852:9 + | +852 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/history.rs:388:9 + | +388 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/particle_view.rs:173:9 + | +173 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/transport_view.rs:148:9 + | +148 | use super::*; + | ^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/viewport.rs:817:9 + | +817 | use super::*; + | ^^^^^^^^ + +warning: unused import: `eframe::egui::Pos2` + --> src/bin/desktop/viewport.rs:818:9 + | +818 | use eframe::egui::Pos2; + | ^^^^^^^^^^^^^^^^^^ + +warning: unused import: `super::*` + --> src/bin/desktop/viewport.rs:1068:9 + | +1068 | use super::*; + | ^^^^^^^^ + | +help: if this is a test module, consider adding a `#[cfg(test)]` to the containing module + --> src/bin/desktop/viewport.rs:1067:1 + | +1067 | mod header_ui_tests { + | ^^^^^^^^^^^^^^^^^^^ + +warning: unused import: `Digest` + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:10:12 + | +10 | use sha2::{Digest, Sha256}; + | ^^^^^^ + +warning: unused import: `Digest` + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:10:12 + | +10 | use sha2::{Digest, Sha256}; + | ^^^^^^ + +warning: function `run` is never used + --> src/bin/rheon-desktop.rs:372:4 + | +372 | fn run( + | ^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: constant `CAP` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:16:7 + | +16 | const CAP: usize = 1024 * 1024; + | ^^^ + +warning: constant `RHO` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:17:7 + | +17 | const RHO: f64 = 2.0; + | ^^^ + +warning: constant `MU` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:18:7 + | +18 | const MU: f64 = 1.0 / 128.0; + | ^^ + +warning: constant `DT` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:19:7 + | +19 | const DT: f64 = 1.0 / 16.0; + | ^^ + +warning: function `grid` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:20:4 + | +20 | fn grid() -> GridGeometry { + | ^^^^ + +warning: function `config` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:23:4 + | +23 | fn config() -> LiquidTransportConfig { + | ^^^^^^ + +warning: function `session` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:45:4 + | +45 | fn session(quota: usize) -> SimulationSession { + | ^^^^^^^ + +warning: function `workspaces` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:60:4 + | +60 | fn workspaces() -> (ProviderStepWorkspace, ViscosityWorkspace) { + | ^^^^^^^^^^ + +warning: function `inputs` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:66:4 + | +66 | fn inputs() -> LiquidStepInputs<'static> { + | ^^^^^^ + +warning: function `edit` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:91:4 + | +91 | fn edit(fixture: &Fixture, f: impl FnOnce(&mut Value)) { + | ^^^^ + +warning: function `package` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:97:4 + | +97 | fn package(correction: bool, behavior: &str) -> Fixture { + | ^^^^^^^ + +warning: function `identity` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:150:4 + | +150 | fn identity(fixture: &Fixture) -> Arc { + | ^^^^^^^^ + +warning: function `start` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:165:4 + | +165 | fn start(fixture: &Fixture) -> (AcceptedCpuProvider, ChildSlot) { + | ^^^^^ + +warning: function `drain` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:178:4 + | +178 | fn drain(provider: AcceptedCpuProvider, slot: ChildSlot) { + | ^^^^^ + +warning: function `received` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:185:4 + | +185 | fn received(f: &Fixture) -> Vec { + | ^^^^^^^^ + +warning: function `state_bits` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:195:4 + | +195 | fn state_bits(v: LiquidTransportView<'_>) -> Vec { + | ^^^^^^^^^^ + +warning: function `publication` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:220:4 + | +220 | fn publication(s: &SimulationSession) -> Vec { + | ^^^^^^^^^^^ + +warning: function `receipt_unchanged` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:225:4 + | +225 | fn receipt_unchanged(a: &ConfiguredProviderReport, b: &ConfiguredProviderReport) { + | ^^^^^^^^^^^^^^^^^ + +warning: function `seed` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_tests.rs:249:4 + | +249 | fn seed( + | ^^^^ + +warning: methods `runtime_ready` and `inlet` are never used + --> src/bin/desktop/ai_comparison/configured_track.rs:307:19 + | + 68 | impl ConfiguredTrack { + | -------------------- methods in this implementation +... +307 | pub(crate) fn runtime_ready(&self) -> bool { + | ^^^^^^^^^^^^^ +... +323 | pub(crate) fn inlet(&self) -> LiquidInlet { + | ^^^^^ + +warning: constant `CAP` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:17:7 + | +17 | const CAP: usize = 1024 * 1024; + | ^^^ + +warning: constant `RHO` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:18:7 + | +18 | const RHO: f64 = 2.0; + | ^^^ + +warning: constant `MU` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:19:7 + | +19 | const MU: f64 = 1.0 / 128.0; + | ^^ + +warning: constant `DT` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:20:7 + | +20 | const DT: f64 = 1.0 / 16.0; + | ^^ + +warning: function `executor` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:21:4 + | +21 | fn executor() -> ExecutorDeclaration { + | ^^^^^^^^ + +warning: function `identity` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:28:4 + | +28 | fn identity(run: u64) -> SessionIdentity { + | ^^^^^^^^ + +warning: function `geometry` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:34:4 + | +34 | fn geometry(counts: [u64; 3]) -> GridGeometry { + | ^^^^^^^^ + +warning: function `configuration` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:37:4 + | +37 | fn configuration() -> LiquidTransportConfig { + | ^^^^^^^^^^^^^ + +warning: function `edit` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:59:4 + | +59 | fn edit(f: &Fixture, action: impl FnOnce(&mut Value)) { + | ^^^^ + +warning: function `package` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:64:4 + | +64 | fn package( + | ^^^^^^^ + +warning: function `pin` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:119:4 + | +119 | fn pin(f: &Fixture) -> ConfiguredProviderPin { + | ^^^ + +warning: function `spec` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:134:4 + | +134 | fn spec( + | ^^^^ + +warning: function `reseal` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:196:4 + | +196 | fn reseal(spec: FrozenConfiguredSpec) -> FrozenConfiguredSpec { + | ^^^^^^ + +warning: function `prepare` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:199:4 + | +199 | fn prepare(spec: FrozenConfiguredSpec, flag: Arc) -> ConfiguredTrack { + | ^^^^^^^ + +warning: function `slot` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:202:4 + | +202 | fn slot() -> ChildSlot { + | ^^^^ + +warning: function `launch` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:205:4 + | +205 | fn launch(track: &mut ConfiguredTrack, f: &Fixture, slot: &ChildSlot) { + | ^^^^^^ + +warning: function `received` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:208:4 + | +208 | fn received(f: &Fixture) -> Vec { + | ^^^^^^^^ + +warning: function `bits` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:218:4 + | +218 | fn bits(v: LiquidTransportView<'_>) -> Vec { + | ^^^^ + +warning: function `publication` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:245:4 + | +245 | fn publication(t: &ConfiguredTrack) -> Vec { + | ^^^^^^^^^^^ + +warning: function `assert_receipt` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:250:4 + | +250 | fn assert_receipt(a: &ConfiguredProviderReport, b: &ConfiguredProviderReport) { + | ^^^^^^^^^^^^^^ + +warning: function `retire` is never used + --> src/bin/desktop/ai_comparison/configured_track_tests.rs:274:4 + | +274 | fn retire(track: &mut ConfiguredTrack, slot: &ChildSlot) { + | ^^^^^^ + +warning: static `SERIAL` is never used + --> src/bin/desktop/ai_comparison/adapter.rs:483:12 + | +483 | static SERIAL: AtomicU64 = AtomicU64::new(0); + | ^^^^^^ + +warning: struct `Fixture` is never constructed + --> src/bin/desktop/ai_comparison/adapter.rs:484:12 + | +484 | struct Fixture { + | ^^^^^^^ + +warning: associated items `new` and `load` are never used + --> src/bin/desktop/ai_comparison/adapter.rs:489:12 + | +488 | impl Fixture { + | ------------ associated items in this implementation +489 | fn new() -> Self { + | ^^^ +... +523 | fn load(&self) -> Result { + | ^^^^ + +warning: function `grid` is never used + --> src/bin/desktop/ai_comparison/adapter.rs:534:8 + | +534 | fn grid(counts: [u64; 3], spacing: [f64; 3], origin: [f64; 3]) -> GridGeometry { + | ^^^^ + +warning: function `assert_child_liveness` is never used + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:20:15 + | +20 | pub(crate) fn assert_child_liveness(pid: u32, expected_alive: bool) { + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: struct `Fixture` is never constructed + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:38:19 + | +38 | pub(crate) struct Fixture { + | ^^^^^^^ + +warning: associated items `new`, `source`, and `edit` are never used + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:43:12 + | +42 | impl Fixture { + | ------------ associated items in this implementation +43 | pub fn new(correction: bool) -> Self { + | ^^^ +... +53 | pub fn source(source: &str, correction: bool) -> Self { + | ^^^^^^ +... +89 | fn edit(&self, f: impl FnOnce(&mut Value)) { + | ^^^^ + +warning: function `slot` is never used + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:101:4 + | +101 | fn slot() -> ChildSlot { + | ^^^^ + +warning: function `input` is never used + --> src/bin/desktop/ai_comparison/cpu_contract_tests.rs:104:4 + | +104 | fn input<'a>( + | ^^^^^ + +warning: constant `CAP` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:34:7 + | +34 | const CAP: usize = 1024 * 1024; + | ^^^ + +warning: constant `RHO` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:35:7 + | +35 | const RHO: f64 = 2.0; + | ^^^ + +warning: constant `MU` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:36:7 + | +36 | const MU: f64 = 1.0 / 128.0; + | ^^ + +warning: constant `DT` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:37:7 + | +37 | const DT: f64 = 1.0 / 16.0; + | ^^ + +warning: function `grid` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:38:4 + | +38 | fn grid() -> GridGeometry { + | ^^^^ + +warning: function `config` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:41:4 + | +41 | fn config() -> LiquidTransportConfig { + | ^^^^^^ + +warning: function `session` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:63:4 + | +63 | fn session(quota: usize) -> SimulationSession { + | ^^^^^^^ + +warning: function `workspaces` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:78:4 + | +78 | fn workspaces() -> (ProviderStepWorkspace, ViscosityWorkspace) { + | ^^^^^^^^^^ + +warning: function `inputs` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:84:4 + | +84 | fn inputs() -> LiquidStepInputs<'static> { + | ^^^^^^ + +warning: function `hash` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:109:4 + | +109 | fn hash(values: &[f64]) -> [u8; 32] { + | ^^^^ + +warning: function `hex_digest` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:116:4 + | +116 | fn hex_digest(value: &str) -> [u8; 32] { + | ^^^^^^^^^^ + +warning: function `publication` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:120:4 + | +120 | fn publication(s: &SimulationSession) -> Vec { + | ^^^^^^^^^^^ + +warning: function `state_bits` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:125:4 + | +125 | fn state_bits(v: LiquidTransportView<'_>) -> Vec { + | ^^^^^^^^^^ + +warning: function `package` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:150:4 + | +150 | fn package(correction: bool, mode: &str) -> Fixture { + | ^^^^^^^ + +warning: function `received` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:194:4 + | +194 | fn received(fixture: &Fixture) -> Vec { + | ^^^^^^^^ + +warning: struct `Observation` is never constructed + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:205:8 + | +205 | struct Observation { + | ^^^^^^^^^^^ + +warning: function `observation` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:213:4 + | +213 | fn observation(r: &ContextPressureRequest<'_>) -> Observation { + | ^^^^^^^^^^^ + +warning: struct `Driver` is never constructed + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:237:8 + | +237 | struct Driver { + | ^^^^^^ + +warning: associated items `new` and `drain` are never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:248:8 + | +247 | impl Driver { + | ----------- associated items in this implementation +248 | fn new(fixture: &Fixture) -> Self { + | ^^^ +... +288 | fn drain(self) { + | ^^^^^ + +warning: function `receipt_unchanged` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:376:4 + | +376 | fn receipt_unchanged(before: &ConfiguredProviderReport, after: &ConfiguredProviderReport) { + | ^^^^^^^^^^^^^^^^^ + +warning: function `check_context` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:403:4 + | +403 | fn check_context(receipt: &ConfiguredProviderReport, observed: &Observation) { + | ^^^^^^^^^^^^^ + +warning: function `direct_input` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:707:4 + | +707 | fn direct_input<'a>(g: &'a GridGeometry, zeros: &'a [f64]) -> Input<'a> { + | ^^^^^^^^^^^^ + +warning: function `drop_process` is never used + --> src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs:726:4 + | +726 | fn drop_process(process: ModelProcess, slot: &ChildSlot) { + | ^^^^^^^^^^^^ + +warning: constant `COLD_PATH` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:15:7 + | +15 | const COLD_PATH: &str = "RHEON_ACCEPTED_RECORDING_COLD_PATH"; + | ^^^^^^^^^ + +warning: constant `COLD_OUTCOME` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:16:7 + | +16 | const COLD_OUTCOME: &str = "RHEON_ACCEPTED_RECORDING_COLD_OUTCOME"; + | ^^^^^^^^^^^^ + +warning: constant `COLD_GENERATION` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:17:7 + | +17 | const COLD_GENERATION: &str = "RHEON_ACCEPTED_RECORDING_COLD_GENERATION"; + | ^^^^^^^^^^^^^^^ + +warning: constant `COLD_LAST_KIND` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:18:7 + | +18 | const COLD_LAST_KIND: &str = "RHEON_ACCEPTED_RECORDING_COLD_LAST_KIND"; + | ^^^^^^^^^^^^^^ + +warning: constant `COLD_SHA` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:19:7 + | +19 | const COLD_SHA: &str = "RHEON_ACCEPTED_RECORDING_COLD_SHA"; + | ^^^^^^^^ + +warning: constant `COLD_SELECTIONS` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:20:7 + | +20 | const COLD_SELECTIONS: &str = "RHEON_ACCEPTED_RECORDING_COLD_SELECTIONS"; + | ^^^^^^^^^^^^^^^ + +warning: function `file_sha` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:22:4 + | +22 | fn file_sha(path: &Path) -> String { + | ^^^^^^^^ + +warning: function `selection_sha` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:25:4 + | +25 | fn selection_sha(reader: &RecordingReader, ordinal: usize) -> String { + | ^^^^^^^^^^^^^ + +warning: function `pressure_report_bits` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:33:4 + | +33 | fn pressure_report_bits(report: PressureReport) -> [u64; 4] { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: function `append_actual` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:41:4 + | +41 | fn append_actual(recorder: &mut StepRecorder, session: &SimulationSession) { + | ^^^^^^^^^^^^^ + +warning: function `new_archive` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:139:4 + | +139 | fn new_archive(path: &Path, session: &SimulationSession) -> StepRecorder { + | ^^^^^^^^^^^ + +warning: function `seed` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:154:4 + | +154 | fn seed( + | ^^^^ + +warning: function `observed_runtime_files` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:177:4 + | +177 | fn observed_runtime_files(fixture: &Fixture) -> [Option>; 2] { + | ^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `cold_reopen` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:190:4 + | +190 | fn cold_reopen(fixture: &Fixture, path: &Path, outcome: &str, generation: u64, last_kind: &str) { + | ^^^^^^^^^^^ + +warning: function `fresh_retry` is never used + --> src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs:338:4 + | +338 | fn fresh_retry( + | ^^^^^^^^^^^ + +warning: method `constructions` is never used + --> src/bin/desktop/ai_comparison/diagnostic_view.rs:47:19 + | +42 | impl AttemptCache { + | ----------------- method in this implementation +... +47 | pub(super) fn constructions(&self) -> usize { + | ^^^^^^^^^^^^^ + +warning: method `load_for_test` is never used + --> src/bin/desktop/ai_comparison/diagnostic_view.rs:113:19 + | +108 | impl Inspector { + | -------------- method in this implementation +... +113 | pub(super) fn load_for_test(&mut self) { + | ^^^^^^^^^^^^^ + +warning: function `producer` is never used + --> src/bin/desktop/ai_comparison/diagnostic_view.rs:431:8 + | +431 | fn producer(p: &Fixture) -> Worker { + | ^^^^^^^^ + +warning: function `render` is never used + --> src/bin/desktop/ai_comparison/diagnostic_view.rs:452:8 + | +452 | fn render( + | ^^^^^^ + +warning: function `text` is never used + --> src/bin/desktop/ai_comparison/diagnostic_view.rs:472:8 + | +472 | fn text(output: &egui::FullOutput) -> String { + | ^^^^ + +warning: function `package` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:8:4 + | +8 | fn package(body: &str) -> Fixture { + | ^^^^^^^ + +warning: function `zero` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:23:4 + | +23 | fn zero() -> Fixture { + | ^^^^ + +warning: function `scene` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:28:4 + | +28 | fn scene() -> Scene { + | ^^^^^ + +warning: function `wait` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:36:4 + | +36 | fn wait(worker: &mut super::super::worker::Worker) { + | ^^^^ + +warning: function `cold` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:44:4 + | +44 | fn cold(worker: super::super::worker::Worker) -> (std::path::PathBuf, DiagnosticRunSet) { + | ^^^^ + +warning: function `ready` is never used + --> src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs:56:4 + | +56 | fn ready(set: &DiagnosticRunSet, path: &Path) -> rheon::recording::diagnostic::DiagnosticReader { + | ^^^^^ + +warning: struct `Archive` is never constructed + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:8:8 + | +8 | struct Archive(PathBuf); + | ^^^^^^^ + +warning: associated function `new` is never used + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:10:8 + | + 9 | impl Archive { + | ------------ associated function in this implementation +10 | fn new() -> Self { + | ^^^ + +warning: function `binding` is never used + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:26:4 + | +26 | fn binding() -> BindingIdentity { + | ^^^^^^^ + +warning: function `native` is never used + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:35:4 + | +35 | fn native(input: Input<'_>) -> Result, PredictionFailure> { + | ^^^^^^ + +warning: function `scene` is never used + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:59:4 + | +59 | fn scene() -> Scene { + | ^^^^^ + +warning: function `warmup` is never used + --> src/bin/desktop/ai_comparison/engine_typed_tests.rs:67:4 + | +67 | fn warmup(e: &mut Engine, flag: &AtomicBool) { + | ^^^^^^ + +warning: function `slot` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:6:4 + | +6 | fn slot() -> ChildSlot { + | ^^^^ + +warning: function `start` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:9:4 + | +9 | fn start(package: &Fixture, flag: &AtomicBool) -> ModelProcess { + | ^^^^^ + +warning: function `input` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:12:4 + | +12 | fn input<'a>(grid: &'a GridGeometry, zero: &'a [f64], prior: Option<&'a [f64]>) -> Input<'a> { + | ^^^^^ + +warning: function `source` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:40:4 + | +40 | fn source(body: &str, correction: bool) -> Fixture { + | ^^^^^^ + +warning: function `packet` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:54:4 + | +54 | fn packet(raw: &str, correction: bool) -> Fixture { + | ^^^^^^ + +warning: function `edit` is never used + --> src/bin/desktop/ai_comparison/prediction_contract_tests.rs:63:4 + | +63 | fn edit(package: &Fixture, change: impl FnOnce(&mut Value)) { + | ^^^^ + +warning: function `wait_for` is never used + --> src/bin/desktop/ai_comparison/worker.rs:355:8 + | +355 | fn wait_for(worker: &Worker, mut ready: impl FnMut(&Shared) -> bool) { + | ^^^^^^^^ + +warning: function `worker` is never used + --> src/bin/desktop/ai_comparison/mod.rs:428:8 + | +428 | fn worker(package: &cpu_contract_tests::Fixture, counts: [usize; 3]) -> Worker { + | ^^^^^^ + +warning: constant `PHASE` is never used + --> src/bin/desktop/configured_comparison_tests.rs:24:7 + | +24 | const PHASE: &str = "RHEON_CONFIGURED_COMPARISON_PHASE"; + | ^^^^^ + +warning: constant `ROOT` is never used + --> src/bin/desktop/configured_comparison_tests.rs:25:7 + | +25 | const ROOT: &str = "RHEON_CONFIGURED_COMPARISON_ROOT"; + | ^^^^ + +warning: constant `PROCESS_TEST` is never used + --> src/bin/desktop/configured_comparison_tests.rs:26:7 + | +26 | const PROCESS_TEST: &str = "configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime"; + | ^^^^^^^^^^^^ + +warning: constant `EPS` is never used + --> src/bin/desktop/configured_comparison_tests.rs:27:7 + | +27 | const EPS: f64 = 1e-12; + | ^^^ + +warning: function `produce` is never used + --> src/bin/desktop/configured_comparison_tests.rs:29:4 + | +29 | fn produce( + | ^^^^^^^ + +warning: function `pressure_recipe` is never used + --> src/bin/desktop/configured_comparison_tests.rs:52:4 + | +52 | fn pressure_recipe( + | ^^^^^^^^^^^^^^^ + +warning: function `check_pressure` is never used + --> src/bin/desktop/configured_comparison_tests.rs:72:4 + | +72 | fn check_pressure(verified: &VerifiedComparison, ordinals: &[usize]) { + | ^^^^^^^^^^^^^^ + +warning: function `selected` is never used + --> src/bin/desktop/configured_comparison_tests.rs:105:4 + | +105 | fn selected<'a>(root: &'a Path, verified: &'a VerifiedComparison) -> SelectedNativePair<'a> { + | ^^^^^^^^ + +warning: function `check_linked` is never used + --> src/bin/desktop/configured_comparison_tests.rs:112:4 + | +112 | fn check_linked(root: &Path, verified: &VerifiedComparison, ordinal: usize) { + | ^^^^^^^^^^^^ + +warning: struct `Harness` is never constructed + --> src/bin/desktop/configured_comparison_tests.rs:151:8 + | +151 | struct Harness { + | ^^^^^^^ + +warning: associated items `new`, `frame`, `at`, `click`, `text`, and `choice` are never used + --> src/bin/desktop/configured_comparison_tests.rs:157:8 + | +156 | impl Harness { + | ------------ associated items in this implementation +157 | fn new(root: &Path, manifest: &str) -> Self { + | ^^^ +... +166 | fn frame(&mut self, events: Vec) -> egui::FullOutput { + | ^^^^^ +... +183 | fn at(&mut self, pos: egui::Pos2) -> egui::FullOutput { + | ^^ +... +200 | fn click(&mut self, id: &str) -> egui::FullOutput { + | ^^^^^ +... +209 | fn text(&mut self, id: &str, value: &str) { + | ^^^^ +... +226 | fn choice(&mut self, id: &str, label: &str) { + | ^^^^^^ + +warning: function `label_position` is never used + --> src/bin/desktop/configured_comparison_tests.rs:232:4 + | +232 | fn label_position(output: &egui::FullOutput, label: &str) -> egui::Pos2 { + | ^^^^^^^^^^^^^^ + +warning: function `shown` is never used + --> src/bin/desktop/configured_comparison_tests.rs:245:4 + | +245 | fn shown(output: &egui::FullOutput) -> String { + | ^^^^^ + +warning: function `inspector_csv` is never used + --> src/bin/desktop/configured_comparison_tests.rs:256:4 + | +256 | fn inspector_csv(root: &Path, revision: &ConfiguredRunSetRevision) { + | ^^^^^^^^^^^^^ + +warning: struct `HiddenArchives` is never constructed + --> src/bin/desktop/configured_comparison_tests.rs:342:8 + | +342 | struct HiddenArchives(Vec<(PathBuf, PathBuf)>); + | ^^^^^^^^^^^^^^ + +warning: associated function `new` is never used + --> src/bin/desktop/configured_comparison_tests.rs:344:8 + | +343 | impl HiddenArchives { + | ------------------- associated function in this implementation +344 | fn new(root: &Path, revision: &ConfiguredRunSetRevision) -> Self { + | ^^^ + +warning: struct `Packet` is never constructed + --> src/bin/desktop/configured_comparison_tests.rs:524:8 + | +524 | struct Packet { + | ^^^^^^ + +warning: struct `ResultPayload` is never constructed + --> src/bin/desktop/configured_comparison_tests.rs:531:8 + | +531 | struct ResultPayload { + | ^^^^^^^^^^^^^ + +warning: function `write_packet` is never used + --> src/bin/desktop/configured_comparison_tests.rs:535:4 + | +535 | fn write_packet(path: &Path, packet: &mut Packet) { + | ^^^^^^^^^^^^ + +warning: function `native_declaration` is never used + --> src/bin/desktop/configured_comparison_tests.rs:541:4 + | +541 | fn native_declaration(recipe: &SavedRecipe) -> SavedRecipe { + | ^^^^^^^^^^^^^^^^^^ + +warning: method `density` is never used + --> src/bin/desktop/configured_controls.rs:97:19 + | +95 | impl ConfiguredControls { + | ----------------------- method in this implementation +96 | #[cfg(test)] +97 | pub(crate) fn density(&self) -> f64 { + | ^^^^^^^ + +warning: constant `CAP` is never used + --> src/bin/desktop/configured_controls.rs:528:11 + | +528 | const CAP: usize = 1024 * 1024; + | ^^^ + +warning: function `inputs` is never used + --> src/bin/desktop/configured_controls.rs:529:8 + | +529 | fn inputs() -> ConfiguredLaunchInputs { + | ^^^^^^ + +warning: function `controls` is never used + --> src/bin/desktop/configured_controls.rs:564:8 + | +564 | fn controls() -> ConfiguredControls { + | ^^^^^^^^ + +warning: struct `Package` is never constructed + --> src/bin/desktop/configured_controls.rs:576:12 + | +576 | struct Package(PathBuf); + | ^^^^^^^ + +warning: associated items `new` and `inspect` are never used + --> src/bin/desktop/configured_controls.rs:578:12 + | +577 | impl Package { + | ------------ associated items in this implementation +578 | fn new() -> Self { + | ^^^ +... +619 | fn inspect(&self, controls: &mut ConfiguredControls) { + | ^^^^^^^ + +warning: constant `STORAGE_TAIL_PROCESS_TEST` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:27:18 + | +27 | pub(super) const STORAGE_TAIL_PROCESS_TEST: &str = "configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model... + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: constant `PHASE` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:28:7 + | +28 | const PHASE: &str = "RHEON_CONFIGURED_STORAGE_PHASE"; + | ^^^^^ + +warning: constant `ROOT` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:29:7 + | +29 | const ROOT: &str = "RHEON_CONFIGURED_STORAGE_ROOT"; + | ^^^^ + +warning: constant `RETAIN_ROOT` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:30:7 + | +30 | const RETAIN_ROOT: &str = "RHEON_CONFIGURED_STORAGE_TAIL_RETAIN"; + | ^^^^^^^^^^^ + +warning: constant `DT` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:31:7 + | +31 | const DT: f64 = 1.0 / 16.0; + | ^^ + +warning: struct `ArtifactPin` is never constructed + --> src/bin/desktop/configured_producer_exit_tests.rs:34:8 + | +34 | struct ArtifactPin { + | ^^^^^^^^^^^ + +warning: struct `CaseReceipt` is never constructed + --> src/bin/desktop/configured_producer_exit_tests.rs:39:8 + | +39 | struct CaseReceipt { + | ^^^^^^^^^^^ + +warning: function `digest` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:51:4 + | +51 | fn digest(path: &Path) -> [u8; 32] { + | ^^^^^^ + +warning: function `artifact_pins` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:54:4 + | +54 | fn artifact_pins(root: &Path) -> Vec { + | ^^^^^^^^^^^^^ + +warning: function `selections` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:76:4 + | +76 | fn selections(reader: &RecordingReader) -> [[u8; 32]; 2] { + | ^^^^^^^^^^ + +warning: function `check_tail` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:79:4 + | +79 | fn check_tail( + | ^^^^^^^^^^ + +warning: function `check_cold_ui` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:164:4 + | +164 | fn check_cold_ui(manifest: &Path, revision: &ConfiguredRunSetRevision, reader: &RecordingReader) { + | ^^^^^^^^^^^^^ + +warning: function `produce` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:233:4 + | +233 | fn produce(root: &Path, correction: bool) -> CaseReceipt { + | ^^^^^^^ + +warning: function `inspect` is never used + --> src/bin/desktop/configured_producer_exit_tests.rs:324:4 + | +324 | fn inspect(root: &Path) { + | ^^^^^^^ + +warning: constant `CAP` is never used + --> src/bin/desktop/configured_worker_tests.rs:20:7 + | +20 | const CAP: usize = 16 * 1024 * 1024; + | ^^^ + +warning: struct `Output` is never constructed + --> src/bin/desktop/configured_worker_tests.rs:21:19 + | +21 | pub(super) struct Output(pub(super) PathBuf); + | ^^^^^^ + +warning: associated function `new` is never used + --> src/bin/desktop/configured_worker_tests.rs:23:19 + | +22 | impl Output { + | ----------- associated function in this implementation +23 | pub(super) fn new() -> Self { + | ^^^ + +warning: function `package` is never used + --> src/bin/desktop/configured_worker_tests.rs:39:15 + | +39 | pub(super) fn package(correction: bool, behavior: &str) -> Fixture { + | ^^^^^^^ + +warning: function `set` is never used + --> src/bin/desktop/configured_worker_tests.rs:51:15 + | +51 | pub(super) fn set(fixture: &Fixture) -> ConfiguredRunSetRevision { + | ^^^ + +warning: function `app` is never used + --> src/bin/desktop/configured_worker_tests.rs:159:15 + | +159 | pub(super) fn app(output: &Output) -> App { + | ^^^ + +warning: function `wait` is never used + --> src/bin/desktop/configured_worker_tests.rs:166:4 + | +166 | fn wait(mut condition: impl FnMut() -> bool) { + | ^^^^ + +warning: function `launch` is never used + --> src/bin/desktop/configured_worker_tests.rs:173:15 + | +173 | pub(super) fn launch(app: &mut App, set: ConfiguredRunSetRevision, root: &Path) { + | ^^^^^^ + +warning: function `finish` is never used + --> src/bin/desktop/configured_worker_tests.rs:177:15 + | +177 | pub(super) fn finish(app: &mut App) -> ConfiguredRunSetRevision { + | ^^^^^^ + +warning: function `member` is never used + --> src/bin/desktop/configured_worker_tests.rs:183:15 + | +183 | pub(super) fn member(root: &Path, set: &ConfiguredRunSetRevision, index: usize) -> RecordingReader { + | ^^^^^^ + +warning: function `signature` is never used + --> src/bin/desktop/configured_worker_tests.rs:191:4 + | +191 | fn signature(reader: &RecordingReader) -> String { + | ^^^^^^^^^ + +warning: function `cold` is never used + --> src/bin/desktop/configured_worker_tests.rs:202:4 + | +202 | fn cold(manifest: &Path, index: usize, reader: &RecordingReader, hidden: &Path) { + | ^^^^ + +warning: method `range` is never used + --> src/bin/desktop/engineering.rs:190:8 + | + 28 | impl SliceData { + | -------------- method in this implementation +... +190 | fn range(&self, pressure: bool) -> [f64; 2] { + | ^^^^^ + +warning: associated functions `new`, `plan`, and `with_budget` are never used + --> src/bin/desktop/history.rs:250:12 + | +248 | impl LiveHistory { + | ---------------- associated functions in this implementation +249 | #[cfg(test)] +250 | pub fn new(grid_edge: usize, steps: usize, tracks: usize) -> Result, String> { + | ^^^ +... +254 | pub fn plan( + | ^^^^ +... +305 | pub fn with_budget( + | ^^^^^^^^^^^ + +warning: methods `selected_identity` and `archive_path` are never used + --> src/bin/desktop/inspector.rs:88:12 + | +58 | impl Inspector { + | -------------- methods in this implementation +... +88 | pub fn selected_identity(&self) -> Option<(u64, u64, f64)> { + | ^^^^^^^^^^^^^^^^^ +... +98 | pub fn archive_path(&self) -> Option { + | ^^^^^^^^^^^^ + +warning: function `test_path` is never used + --> src/bin/desktop/inspector.rs:756:12 + | +756 | pub fn test_path(label: &str) -> PathBuf { + | ^^^^^^^^^ + +warning: function `object` is never used + --> src/bin/desktop/scene_handles.rs:544:8 + | +544 | fn object() -> ForceObject { + | ^^^^^^ + +warning: function `scene` is never used + --> src/bin/desktop/scene_handles.rs:562:8 + | +562 | fn scene() -> SceneUi { + | ^^^^^ + +warning: function `rect` is never used + --> src/bin/desktop/scene_handles.rs:573:8 + | +573 | fn rect() -> Rect { + | ^^^^ + +warning: function `screen` is never used + --> src/bin/desktop/scene_handles.rs:576:8 + | +576 | fn screen(camera: &OrbitCamera, rect: Rect, p: V, side: f64) -> Pos2 { + | ^^^^^^ + +warning: struct `Harness` is never constructed + --> src/bin/desktop/scene_handles.rs:685:12 + | +685 | struct Harness { + | ^^^^^^^ + +warning: associated items `new`, `frame`, `point`, `button`, and `press` are never used + --> src/bin/desktop/scene_handles.rs:696:12 + | +695 | impl Harness { + | ------------ associated items in this implementation +696 | fn new(tool: Tool) -> Self { + | ^^^ +... +719 | fn frame( + | ^^^^^ +... +762 | fn point(&self, x: f64) -> Pos2 { + | ^^^^^ +... +765 | fn button(&mut self, p: Pos2, pressed: bool, modifiers: egui::Modifiers) { + | ^^^^^^ +... +781 | fn press(&mut self) -> Pos2 { + | ^^^^^ + +warning: associated function `geometry_fixture` is never used + --> src/bin/desktop/scene_ui.rs:67:19 + | +65 | impl SceneUi { + | ------------ associated function in this implementation +66 | #[cfg(test)] +67 | pub(super) fn geometry_fixture(forces: ForceScene, model: bool) -> Self { + | ^^^^^^^^^^^^^^^^ + +warning: function `context` is never used + --> src/bin/desktop/scene_ui.rs:457:8 + | +457 | fn context() -> egui::Context { + | ^^^^^^^ + +warning: function `frame` is never used + --> src/bin/desktop/scene_ui.rs:463:8 + | +463 | fn frame( + | ^^^^^ + +warning: function `text_rect` is never used + --> src/bin/desktop/scene_ui.rs:492:8 + | +492 | fn text_rect(output: &egui::FullOutput, label: &str, last: bool) -> egui::Rect { + | ^^^^^^^^^ + +warning: function `click` is never used + --> src/bin/desktop/scene_ui.rs:511:8 + | +511 | fn click( + | ^^^^^ + +warning: function `key` is never used + --> src/bin/desktop/scene_ui.rs:537:8 + | +537 | fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + | ^^^ + +warning: function `replace` is never used + --> src/bin/desktop/scene_ui.rs:548:8 + | +548 | fn replace(ctx: &egui::Context, scene: &mut SceneUi, width: f32, text: &str) { + | ^^^^^^^ + +warning: function `force` is never used + --> src/bin/desktop/scene_view.rs:668:8 + | +668 | fn force(id: u64, kind: ForceKind) -> ForceObject { + | ^^^^^ + +warning: function `camera` is never used + --> src/bin/desktop/scene_view.rs:683:8 + | +683 | fn camera() -> OrbitCamera { + | ^^^^^^ + +warning: function `rect` is never used + --> src/bin/desktop/scene_view.rs:691:8 + | +691 | fn rect() -> Rect { + | ^^^^ + +warning: function `state` is never used + --> src/bin/desktop/scene_view.rs:694:8 + | +694 | fn state(objects: Vec, model: bool) -> SceneUi { + | ^^^^^ + +warning: function `context` is never used + --> src/bin/desktop/settings.rs:393:8 + | +393 | fn context() -> egui::Context { + | ^^^^^^^ + +warning: function `frame` is never used + --> src/bin/desktop/settings.rs:398:8 + | +398 | fn frame( + | ^^^^^ + +warning: function `text_rect` is never used + --> src/bin/desktop/settings.rs:443:8 + | +443 | fn text_rect(output: &egui::FullOutput, label: &str) -> Option { + | ^^^^^^^^^ + +warning: function `pointer` is never used + --> src/bin/desktop/settings.rs:451:8 + | +451 | fn pointer(pos: egui::Pos2, pressed: bool) -> Vec { + | ^^^^^^^ + +warning: function `key` is never used + --> src/bin/desktop/settings.rs:462:8 + | +462 | fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + | ^^^ + +warning: function `click` is never used + --> src/bin/desktop/settings.rs:473:8 + | +473 | fn click( + | ^^^^^ + +warning: function `material_bits` is never used + --> src/bin/desktop/settings.rs:492:8 + | +492 | fn material_bits(controls: &MaterialControls) -> [u64; 6] { + | ^^^^^^^^^^^^^ + +warning: function `numeric_label` is never used + --> src/bin/desktop/settings.rs:506:8 + | +506 | fn numeric_label(output: &egui::FullOutput, suffix: &str) -> String { + | ^^^^^^^^^^^^^ + +warning: function `replace_focused` is never used + --> src/bin/desktop/settings.rs:524:8 + | +524 | fn replace_focused( + | ^^^^^^^^^^^^^^^ + +warning: function `frame` is never used + --> src/bin/desktop/setup_ui_tests.rs:4:4 + | +4 | fn frame( + | ^^^^^ + +warning: function `labels` is never used + --> src/bin/desktop/setup_ui_tests.rs:19:4 + | +19 | fn labels(output: &egui::FullOutput) -> Vec<(String, egui::Rect)> { + | ^^^^^^ + +warning: function `has` is never used + --> src/bin/desktop/setup_ui_tests.rs:35:4 + | +35 | fn has(output: &egui::FullOutput, label: &str) -> bool { + | ^^^ + +warning: function `rect` is never used + --> src/bin/desktop/setup_ui_tests.rs:38:4 + | +38 | fn rect(output: &egui::FullOutput, label: &str) -> egui::Rect { + | ^^^^ + +warning: function `click` is never used + --> src/bin/desktop/setup_ui_tests.rs:45:4 + | +45 | fn click( + | ^^^^^ + +warning: function `context` is never used + --> src/bin/desktop/setup_ui_tests.rs:70:4 + | +70 | fn context() -> egui::Context { + | ^^^^^^^ + +warning: function `context` is never used + --> src/bin/desktop/timeline_ui_tests.rs:5:4 + | +5 | fn context() -> egui::Context { + | ^^^^^^^ + +warning: function `frame` is never used + --> src/bin/desktop/timeline_ui_tests.rs:14:4 + | +14 | fn frame( + | ^^^^^ + +warning: function `text_rect` is never used + --> src/bin/desktop/timeline_ui_tests.rs:55:4 + | +55 | fn text_rect(output: &egui::FullOutput, label: &str) -> egui::Rect { + | ^^^^^^^^^ + +warning: function `assert_visible` is never used + --> src/bin/desktop/timeline_ui_tests.rs:67:4 + | +67 | fn assert_visible(output: &egui::FullOutput, panel: egui::Rect) { + | ^^^^^^^^^^^^^^ + +warning: function `assert_visible_with_editor` is never used + --> src/bin/desktop/timeline_ui_tests.rs:70:4 + | +70 | fn assert_visible_with_editor(output: &egui::FullOutput, panel: egui::Rect, editor: Option<&str>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `click` is never used + --> src/bin/desktop/timeline_ui_tests.rs:127:4 + | +127 | fn click( + | ^^^^^ + +warning: function `inert_carrier_request` is never used + --> src/bin/desktop/timeline_ui_tests.rs:248:4 + | +248 | fn inert_carrier_request() -> Request { + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: function `presentation` is never used + --> src/bin/desktop/timeline_ui_tests.rs:274:4 + | +274 | fn presentation() -> PreviewFrame { + | ^^^^^^^^^^^^ + +warning: function `key` is never used + --> src/bin/desktop/timeline_ui_tests.rs:288:4 + | +288 | fn key(key: egui::Key, modifiers: egui::Modifiers) -> Vec { + | ^^^ + +warning: function `replace_speed` is never used + --> src/bin/desktop/timeline_ui_tests.rs:299:4 + | +299 | fn replace_speed(ctx: &egui::Context, app: &mut App, selected: &PreviewFrame, text: &str) { + | ^^^^^^^^^^^^^ + +warning: method `show` is never used + --> src/bin/desktop/viewport.rs:134:12 + | +122 | impl Viewport { + | ------------- method in this implementation +... +134 | pub fn show( + | ^^^^ + +warning: function `saved_native_worker_case` is never used + --> src/bin/rheon-desktop.rs:2798:8 + | +2798 | fn saved_native_worker_case(high_density: bool) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: function `wait_until` is never used + --> src/bin/rheon-desktop.rs:3376:8 + | +3376 | fn wait_until(mut condition: impl FnMut() -> bool) { + | ^^^^^^^^^^ + +warning: `rheon` (bin "rheon-desktop") generated 258 warnings (run `cargo fix --bin "rheon-desktop" -p rheon` to apply 41 suggestions) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.80s diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index f753ce00..5e68a2cd 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -418,3 +418,41 @@ model manipulation/force runtime/archive test occurred here. Native handoff: reports/native-force-handles-handoff.md. Force-enabled run refusal remains. Next: separately reviewed workerless synthetic retained/timeline presentation fixture, then agreed T5 runtime/immutable archive custody; no scientific workload authorized. + +## Test-only synthetic retained presentation handoff —2026-10-20 + +Separate increment after425a986. No production CLI/launch feature, Cargo/dependency +change or native run. cfg(test) module/main supplies a main-thread standalone native +presentation build; ordinary cargo test uses its generated runner and enumerates only +ONE pure harness probe (190total). The exact pure probe passed; literal4×8-byte frames, +metadata-only legal Request, in-memory history/default readerNone, no Worker/solver/ +model/recorder/capture. Existing Appearance rendering is disabled and additionally +hard-guarded before numerical-mode dispatch. This is not a physics/archive fixture. +Source and plan independently reviewed, dark8×7 geometry/sidebar reservation and +post-control frame refetch match production. Synthetic/unmeasured-time annotations +persist. Monotonic300s close is cooperative, external native supervision required. + +Standalone `cargo rustc ... -- --cfg test` without --test compiled; final guarded +binary uniquely copied/sealed before ordinary production build restored normal target. +235185520bytes SHA040715719f4e34e04951c8ad428024cbdbd219d7b65ac95d64d54e3e6e8d5cbd, +never executed.258unused-code/import warnings in this special cfg(test) build are +retained, no suppression/fix. Ordinary production build/strictClippy/check/fmt/diff +pass. Three source seals and9raw logs (8accepted plus initialcompilefailure) are in +evidence/synthetic-presentation-20261020/manifest.json SHA +72df6b803271253cdaede05edfcf48cf9b6318a2fc61946a61abdca18b1e09c8. +Initial compile rejected App Drop struct-update and unavailable eframe winit reexport; +corrected by value-only assignments and main-thread standalone design, no dependency +or permission change. Earlier unguarded artifact preserved but excluded from handoff. + +Nativeharness procedure: reports/native-synthetic-presentation-handoff.md. No native +Play/Pause/speed/track/retained↔Draft screenshot, canvas/key interaction usability, +worker/cancellation or scientific acceptance is claimed. Original6127 first-session +stdout overwrite and corrected capture labels remain explicit. JPEG/Library delivery +is still owned by existing validator; this owner invents no image/Library identity. + +Next T5 preflight identifies Inspector::begin/record as a necessary snapshot-binding +seam beyond the agreed Request/carrier/run-set write set. Minimal interface proposal +is in reports/runtime-force-integration-admission.md; coordination is pending before +Inspector edits. No numerical workload or force launch was enabled. Independent pure +versioned intent-codec work may follow after that interface/dependency agreement; +preserve legacy global manifest/schema/hash contracts rather than blind version bump. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index e71ba619..20fece6a 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T4 bounded source checkpoint; native validation separate -**Next slice:** workerless native presentation harness, then T5 runtime/archive custody +**Current phase:** T4 source published; synthetic retained presentation handoff ready +**Next slice:** T5 interface coordination and pure versioned frozen-intent codecs **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -98,6 +98,7 @@ Inspector slice before any overlapping changes; T1 excludes Inspector entirely. | T2 | new desktop/scene_ui.rs; main App scene composition/context/model-ID transactions and draft launch refusal; exact inert scene tests; viewport.rs mode-control guard only (no lab dispatch/picking changes) | Canonical domain received; reviewed [authoring admission](reports/scene-authoring-admission.md); one ForceScene/selection owner; viewport mode refusal only, no runtime dispatch | Completed (bounded source) | | T3 | new scene_view.rs geometry/picking; viewport.rs aggregate-input composition and thin geometry outline; existing SceneUi selection and main routing | [Exact selection admission](reports/viewport-selection-admission.md), inert camera/geometry/UI tests; no lab initialization, then native | Bounded source complete; native pending | | T4 | desktop/scene_handles.rs, viewport exclusive gesture routing, scene_ui validated draft transaction; force-only (model placement deferred) | [Force-only handle admission](reports/force-handles-admission.md); same IDs/domain types; no physics; exact Escape rollback and terminal-frame ownership | Bounded source complete; native pending | +| T4a | cfg(test) native_presentation_harness.rs, cfg-gated main and viewport containment; no production launch/Cargo change | [Workerless contract](reports/synthetic-retained-presentation-contract.md), one exact pure UI probe, standalone compile/sealed artifact; existing validator native only | Bounded source complete; native pending | | T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | @@ -215,3 +216,13 @@ long captions, popup keyboard/window bounds and native behavior remain unaccepte No complete UI, force parity, native/accessibility, M1 or scientific acceptance is claimed. Accept only exact staged source/test/native evidence once its objective criteria are met. Publish/merge only after separate coordination/authorization. + +## T5 interface preflight + +[Runtime/archive boundary](reports/runtime-force-integration-admission.md) records +agreed Request/dispatch/run-set ownership and the newly identified minimal Inspector +recorder entry-point coordination. No Inspector, core or runtime T5 source changed. +Force launch refusal remains until corresponding runtime/archive custody is wired +and validated. Native425a986 handles and the separately labelled synthetic timeline +harness have distinct evidence; neither accepts worker cancellation or scientific +runtime parity. Preserve historical hashes and legacy spec versions exactly. diff --git a/docs/plans/ui-refinement/reports/native-synthetic-presentation-handoff.md b/docs/plans/ui-refinement/reports/native-synthetic-presentation-handoff.md new file mode 100644 index 00000000..993267e4 --- /dev/null +++ b/docs/plans/ui-refinement/reports/native-synthetic-presentation-handoff.md @@ -0,0 +1,58 @@ +# Workerless native retained/timeline handoff + +Use the separate published harness checkpoint after425a986, exact Git commit/tree +and three source seals from evidence/synthetic-presentation-20261020/manifest.json. +No existing DB reconstruction, worker, solver, model, recorder, run/Cancel or rerun. +This verifies presentation using two tracks×two ordered literal2³preview frames, +supplied timestamps0/0.25s and unmeasured calculation duration; never call scientific +or archive/cancellation evidence. Persistent synthetic annotation stays visible. + +Build/verify in the existing authorized native environment: + +```sh +cargo test --locked --offline --features desktop --bin rheon-desktop -- --list +cargo test --locked --offline --features desktop --bin rheon-desktop native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless -- --exact --test-threads=1 +cargo rustc --locked --offline --features desktop --bin rheon-desktop -- --cfg test +``` + +The third command does NOT use --test: it creates the standalone cfg(test) main-thread +presentation entry. Normal cargo test uses the compiler-generated unit-test runner; +the native function is not #[test], so enumeration must show only the one pure probe +in this module. Normal cfg(not(test)) main keeps its original production body. +No Cargo/lock/dependency change or production CLI/env switch was added. + +Immediately copy the emitted target/debug/rheon-desktop into a uniquely named sealed +path outside the normal target filename and record its SHA256/size/config/source. +Then run the ordinary locked offline desktop cargo build to restore the normal +production target before other use. Execute ONLY the uniquely sealed harness path +under the validator's existing native process supervision. Resizing is native; +default1250×820, minimum600×430. A Close button is always outside the disabled canvas. +The monotonic300s self-close is cooperative, not a hard process limit: external +supervision remains required. No native launch occurred in this source environment. + +Local compiled final-guard artifact (not a cross-environment reproducibility claim): +`/workspace/scratch/rheon-ui-presentation/rheon-ui-synthetic-presentation-testcfg-guarded-425a986-increment.bin` +235185520bytes; SHA256 +040715719f4e34e04951c8ad428024cbdbd219d7b65ac95d64d54e3e6e8d5cbd. +Never executed. Earlier pre-guard artifact is preserved but is NOT the handoff target. +Standalone build emits258unused-code/import warnings from cfg(test) helpers outside +a test runner; no warning suppression or automatic fix. Ordinary production restore, +strict production Clippy and check pass cleanly. Do not call the native cfg build +strict-lint clean. Native builds should seal their own binary, not assume identical +hash across paths/toolchain environments. + +At1250×820/900×650 and100/150% scale, capture actual content rect/scale and Scene state. +Default follow_live=false,track/index0; dark8×7 style,280px sidebar reservation except +open narrow Scene. Check Play/Pause, current frame/timestamp/track, slider endpoints, +speed text editing/tab/focus/bounds, track dropdown/retained selection. Switch Draft: +playback pauses, timeline hides and retained index is preserved. Return Retained: +track/index and literal frame remain coherent. Scene hide/reopen and long captions +should keep compact controls. The wrapper refetches selected frame after controls as +production does. The central actual Appearance renderer is disabled and hard-guarded +before any numerical lab dispatch: this harness cannot validate viewport gestures. +The pure probe executes disabled tab clicks, not native canvas/key input usability. + +Capture actual quality85JPEGs labelled exact source, size/scale and SYNTHETIC state. +Do not represent them as a simulated run or use them for normal viewport/force-handle +validation; use the production425a986+ checkpoint for those separate idle gestures. +Save confirmed Library IDs only when actual uploads succeed; no local IDs are claimed. diff --git a/docs/plans/ui-refinement/reports/runtime-force-integration-admission.md b/docs/plans/ui-refinement/reports/runtime-force-integration-admission.md new file mode 100644 index 00000000..433a112a --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-integration-admission.md @@ -0,0 +1,53 @@ +# T5 runtime/archive force integration boundary (preflight only) + +UI owner: Request/freeze/launch/run_material in rheon-desktop.rs; +carrier_execution.rs dispatch; run_sets.rs and run_sets/{source.rs,configured.rs, +configured/archive.rs} desktop frozen intent/load/rerun. Backend PR145 owns canonical +ForceScene/ForceSnapshot/evaluator/adapters and core recording custody. Reuse exact +canonical ancestor43ec505; no duplicate force model or evaluator. No T5 source edit, +solver, SQLite workload or force-enabled launch is admitted by this preflight. + +Inspected runtime seams: CarrierExecution::advance currently uses legacy step or +step_with_emitter; run_material uses legacy particle step and Inspector::begin/ +record. Carrier recorder likewise uses Inspector owners. Core force-aware APIs now +exist, but these desktop wrappers currently cannot bind the expected snapshot at +run/constructor creation. Therefore runtime wiring needs a minimal additional +Inspector interface contract before editing that file, which has separate validated +projection/archive work. Do not copy/merge/rebase that separate slice speculatively. + +Proposed minimal Inspector addition (coordination pending): explicit begin-carrier/ +begin-material-with-force-fields paths (or one typed optional snapshot argument) +construct the existing RecordingRun.with_force_fields before Recorder creation and +use matching constructor publication wrapper; record paths retain existing owner +and call expected-snapshot publication wrappers. Preserve reader attach, errors, +recording completeness/integrity distinctions and historical legacy paths. No +projection UI change. Signature/write ownership must be agreed with Inspector owner +before source edits; backend core recording remains untouched. + +Once interface is coordinated, freeze the draft before Worker/output allocation, +retain one immutable Request snapshot for the entire run/history, and serialize its +canonical scene for intent/record metadata. Dispatch ONLY native carrier and native +particle-material through their canonical force adapters; legacy None paths retain +exact existing calls. Empty authored document has an explicit policy (None for an +unchanged legacy workflow; a retained Some(empty) document must bind its metadata). +Configured/session/provider/transport/free-surface remain explicitly refused until +those existing owners carry force custody; never route around them. Existing lab +preview modes are not a substitute for two-solver runtime integration. + +Versioning must preserve historical hashes, constructor validation and reruns. +Current FrozenSpec schema and global manifest version share1; do not blindly bump +all manifest formats or add serialized null fields that alter old spec digests. +Prepare an explicit versioned force-bearing frozen spec and bounded canonical scene +bytes, reject mixed/unknown/mismatched versions, keep legacy version1 reserialization +and digest behavior exactly. Archive cold-open/rerun uses recorded run scene, never +current draft. Disabled objects/IDs/order/units/transforms/support/time are retained. +Material run/history requires equivalent immutable metadata even without a saved-set +UI. Reuse D1 comparison; metadata differences become explicit, not reimplemented. + +First independent source-only slice after coordination: pure versioned intent codecs, +legacy golden digest/roundtrip/refusal probes using literal bounded documents only. +Inspect helper paths, list-only, individual --exact. Compile actual dispatch later; +no solver/model/SQLite fixture execution under the current no-workload authority. +Separate bounded desktop/recording integration execution contracts and authorization +are needed before enabling launch readiness. Source compilation alone cannot accept +runtime/archive custody, genuine cancellation or coherent two-solver behavior. diff --git a/docs/plans/ui-refinement/reports/synthetic-retained-presentation-contract.md b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-contract.md new file mode 100644 index 00000000..9893bb5c --- /dev/null +++ b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-contract.md @@ -0,0 +1,56 @@ +# Workerless retained/timeline presentation harness contract + +Authorized by parent after exact6127 native idle validation. Separate from T4 +production handles and from archive reconstruction. No existing DB is opened or +reinterpreted as a running App scene. Sole UI source owner; reviewer owns report. + +Proposed test-only file desktop/native_presentation_harness.rs, root module under +cfg(test) only. No production CLI flag or default launch. One ordinary exact pure construction/Context probe. A separate standalone +`cargo rustc --locked --offline --features desktop --bin rheon-desktop -- --cfg test` +build WITHOUT --test uses the cfg(test) main-thread wrapper; normal cfg(not(test)) +production main is unchanged and has no harness CLI/env switch. Native execution remains with existing validator. Native window900×650 or +1250×820, existing100/150% scale, Close control and cooperative self-close after300seconds +requesting repaint at100ms while idle. External validator retains normal process +supervision; wrapper cannot create worker/child/solver/model/recorder or file output. + +Frozen legal Request: size2,steps1,methods[JacobiPcgV1,SymmetricGaussSeidelPcgV1], +threads1,side1m,dt0.25s,preview Edge2,history budget16MiB (planning limit, not allocated +physical solver storage); no material/emitter/model/configured/saved intent, +observer/child/gates/receipt/prior run IDs. Recording directory only a nonempty +unused sentinel string; nominal valid limits65536database/16384record bytes,2records. +Validate metadata only (GridGeometry/BufferPlan arithmetic, no Simulation creation). +History uses existing in-memory bounded LiveHistory::with_resolution and publish; +2tracks×2ordered literal PreviewFrames, edge2 and8densitybytes each,step[0,1], +time[0,0.25]s; report/particle_report/material_color None,seconds0 (no timing claim). +Different fixed display densities distinguish frame and track. No capture helpers. + +Use App's existing selected_frame/scene_timeline_ui, not App.update/sidebar/poll/ +start/run/rerun or saved/archive views. Default Inspector has no reader; selected +frame selection may report absent archive but must reach no I/O. Wrapper labels +“Synthetic presentation data · no worker or physics” persistently. Scene context +switches pause hidden playback and preserve retained index using the same owner +logic. Advance play_time only while retained/playing, using bounded stable_dt; +track popup and physical timestamps come from the frozen Request/history. + +Center may use the existing default Appearance renderer with UI disabled throughout +so no numerical lab mode/control/camera action can be entered. It paints the literal +2³preview only; no workload/evaluator is reachable. This disabled presentation +canvas is explicitly synthetic and does not validate normal viewport gestures, +physics, recording, cancellation, retained force provenance or runtime/archive +parity. Right Scene may select Draft/Retained, hide/reopen; no model exists. Tests +and native operator must not invoke any production run action (none is composed). + +Evidence: independently inspect complete construction/render/helper path, list-only +then one individually exact pure UI test. Compile the standalone test-configuration native entry without +executing it here; immediately copy/seal a unique artifact before another build; source/patch seals and exact invocation handed to validator. +Actual native Play/Pause/speed/track/slider/Draft↔Retained tests and quality85JPEGs +must identify synthetic presentation state and source revision. Never describe +these as simulated scientific frames, worker cancellation or archive verification. + +Layout/evidence admission: initialize follow_live=false,track/index0 despite last +publication track1; match dark8×7 style and280px sidebar reservation except open +narrow Scene. Re-fetch selected frame after timeline controls as production does. +Keep supplied timestamps and unmeasured calculation caption annotation visible. +No persistence hooks; persist_window=false. Deadline starts once and is cooperative, +not a hard containment claim; native validator supervises process/window closure. +The default native window1250×820 can be manually resized; no size env switch. diff --git a/docs/plans/ui-refinement/reports/synthetic-retained-presentation-plan-review.md b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-plan-review.md new file mode 100644 index 00000000..085e0dd4 --- /dev/null +++ b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-plan-review.md @@ -0,0 +1,140 @@ +# Independent synthetic retained-presentation plan review + +## Disposition + +Conditionally suitable as a separately reviewed, test-only presentation harness. +No source evidence requires a worker, archive reconstruction or numerical state +to exercise the proposed timeline. Keep the layout/evidence and containment +conditions below in the implementation. This admits construction and source +review; it does not accept a harness that has not yet been written or executed. + +## Reviewed source and limits + +Worktree `/workspace/Rheon-ui-authoring`, HEAD +`8504b3e7c9e6a02a6e2521b54c1aefc9c0f58ec2`, with T4 production changes locally +modified. Reviewed `reports/synthetic-retained-presentation-contract.md`, SHA-256 +`7983ab9a9a5953aa5ae3a8a6b02c6099e03941764f7a65f22e29cb5087c2451d`, +against `Request`, `validate_request`, `App::default`, `selected_frame`, +`scene_timeline_ui`, `timeline_ui`, `App::drop`, history construction/publication, +default Inspector selection, scene composition, Appearance routing and existing +isolated timeline test composition. No harness implementation existed at the +initial review; the entry-point refinement below records the later source check. + +No build, test, native window, screenshot, workload or archive operation was run. +An attempted read of dependency `egui` response/UI source beneath +`/root/.cargo/registry/src` was denied by filesystem permissions. It was not +retried. The review therefore does not certify dependency-level disabled-widget +input semantics; exact local Context probes and the existing native validator +remain required. This does not block review of the application's entry points. + +## Conditions and evidence fidelity + +| Priority | Condition | Required evidence | +| --- | --- | --- | +| Medium | Match available production layout width and style. Omitting the production sidebar creates different timeline wrapping. | Reserve an inert 280 px left panel where production would show setup, without calling `sidebar`; use the actual open-Scene/narrow layout decision and panel order. Apply production dark style and 8×7 item spacing. If an alternate composition is retained, label its screenshots harness-only and exclude production wrapping/compactness claims. | +| Medium | Literal metadata must remain visibly synthetic. Existing timeline captions still say “Simulated” and “Step calculation” with absent reports. | Keep the banner persistent in every captured state; explain that timestamps/densities are supplied literals and zero calculation duration is unmeasured. Do not interpret those captions as execution or timing evidence. No production copy change is necessary for this harness. | +| Medium | Disabled presentation UI must be a containment boundary, not merely an instruction to the operator. | Construct a fresh default Appearance viewport, keep its entire composition disabled every frame, with no user-accessible mode setter or Inspector UI. A narrow `cfg(test)` Appearance reset/inert-state query is allowed. Inspect complete rendering/helper paths and probe attempted tab/canvas/key input in local Context. No `App::update`, sidebar, polling, run helpers or archive/saved view may be composed. | +| Low | Publication and default App state otherwise choose live/latest history rather than the intended first frame. | Explicitly set `follow_live=false`, track/index/play time and paused initial state. Publishing track 1 last updates `active_track`; do not rely on defaults. Check each frame's method, ordered step/time, exact edge/density length and distinguishable literals. | + +## Ownership and containment + +The only new application file is the proposed test module, plus a root +`cfg(test)` module declaration/main and narrow test-only viewport guard/query. +These additions must stay test-only. The original proposed ignored native test +is superseded by the standalone test-configured main below; the ordinary probe +uses only construction and local egui Context frames. No production launch flag, +default startup change, runtime adapter, force integration or archive +reconstruction belongs here. + +The proposed request is legal metadata: two distinct pressure method identifiers, +size 2, one requested step, one thread, 1 m domain, 0.25 s duration, exact preview +edge 2 and 16 MiB planning budget. In the specified absent-material/emitter/model/ +configured/saved case, `validate_request` performs limit/geometry/history/buffer +arithmetic without constructing a simulation. `LiveHistory::with_resolution` +reserves bounded preview metadata storage; four directly constructed eight-byte +densities are sufficient. Do not use `capture`, `capture_state`, +`capture_particles`, timing helpers, scheduler construction or any run helper. + +Default history Inspectors contain no reader. `selected_frame` reaches +`ensure_timestep` and then returns absent-recording feedback before any read. Keep +that state: never reload an Inspector, attach a reader, compose export controls or +interpret absence as verified recording completeness. Request recording path is +an unused sentinel string, not an artifact destination. No app-owned recording, +export, screenshot, trace or persistence output hook may be added. External native +validator screenshots remain separately owned evidence; normal display-system +initialization is outside this application-data I/O assertion. + +## Presentation and lifecycle contract + +Use the existing selection/timeline methods with one canonical App/SceneUi owner. +Scene context changes pause playback; while Draft, neither hidden selection nor +play time advances. Returning to Retained preserves the retained index. Apply +bounded stable delta only while Retained and playing, and let existing end-of-track +behavior stop playback. Track changes, slider/step actions, invalid speed editing +and Play/Pause are actual timeline interactions; disabled viewport gestures, +force handles and worker cancellation are outside the evidence. + +Render the persistent synthetic label and Close control outside the disabled +canvas. Initialize one monotonic 300-second deadline; interaction must not extend +it. Request repaint while idle so self-close is reached, and keep external native +process supervision. No custom teardown should call run, join or I/O helpers; +worker remains absent, so existing `App::drop` has nothing to cancel/join. App +ownership ends normally when the wrapper closes. Do not add unconditional process +exit or cleanup/deletion logic. + +## Acceptance and remaining gates + +Review the complete new construction/render/helper path before list-only +enumeration and a single fully qualified exact ordinary probe. Check literal +request/history shape, no worker/scheduler/reader/runtime intent, intended initial +selection, Draft pause/index retention, track/time display and disabled lab-mode +attempts. Compile the standalone native entry without running it here. Source review, +format/lint/build evidence and exact invocation/patch seals precede transfer. + +Only the existing validator may run the sealed standalone entry under its authorized +display session. Capture exact source revision, window size/scale, synthetic +banner, Play/Pause/speed/track/slider and Draft↔Retained results, plus Close/deadline +outcome. Such evidence can support synthetic timeline presentation and its +composed layout; it cannot establish normal App startup/sidebar integration, +viewport interaction, runtime/recording/archive parity, force provenance or any +scientific behavior. Rollback is deletion/reversion of this separate test-only +increment, preserving T3/T4 production work and existing native evidence. + +## Standalone main-thread entry refinement + +Later read-only source check: HEAD +`425a9869fa3f36b7e3133ddbd213a493d7b5bab3`, with the harness changes still local. +The source places the unchanged normal main body behind `cfg(not(test))` and adds +a `cfg(test)` main that calls `run_native_presentation`. The new native function +has no test attribute. This is a proportionate way to enter eframe on the process +main thread without a winit dependency or cross-thread extension trait. + +Standard `cargo test` uses its generated test runner; it must never launch this +native main. The new module should enumerate exactly one ordinary pure Context +test and no native test entry. Existing unrelated tests still exist in the +binary's test suite: enumeration is not permission to execute them. Keep the +individual fully qualified exact-test workflow. + +The standalone compile gate is `cargo rustc --locked --offline --features desktop +--bin rheon-desktop -- --cfg test`, without `--test`; success has not been checked +by this reviewer. Use a dedicated target directory for this recipe, or immediately +seal a uniquely named copy before any normal build can replace it. A standalone +test-configured artifact shares the ordinary binary name and must not be +mistaken for a production build. Record source revision/patch seal, full recipe, +toolchain and executable hash. The existing validator executes only that sealed +path, under separate native authorization; no execution is admitted here. + +The build gate must prove this test configuration compiles without adding Cargo +dependencies/features or broad lint suppression, and an ordinary non-test build +must retain the original main behavior. `cfg(test)` also includes other test-only +modules/helpers; their presence is not a workload, but the new main must call +only the inspected presentation construction/update path. Do not use a test-suite +invocation or application `--run` flag to enter native presentation. + +At this check the contract still described the superseded ignored test. Align +that contract before transfer. The implementation also read the test-only +`RHEON_PRESENTATION_SIZE` environment variable, whereas the latest delegated +refinement specified no environment switch. Remove that selector and resize the +native window normally, or explicitly admit that narrow size selector in the +contract before transfer. This is an isolation/documentation condition, not +evidence of a runtime or physics entry. No new native/build/test result is claimed. diff --git a/docs/plans/ui-refinement/reports/synthetic-retained-presentation-source-review.md b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-source-review.md new file mode 100644 index 00000000..900a18f5 --- /dev/null +++ b/docs/plans/ui-refinement/reports/synthetic-retained-presentation-source-review.md @@ -0,0 +1,65 @@ +# Independent synthetic retained-presentation source review + +## Disposition + +Suitable as a separate test-only synthetic presentation harness, with the evidence limits below. The inspected construction and composed UI paths do not start a worker, numerical/model workload, recorder, child process or archive read. This is source acceptance and a receipt for the narrowly executed pure Context probe; native presentation and interaction acceptance remain pending. + +Reviewed in `/workspace/Rheon-ui-authoring` over HEAD `425a9869fa3f36b7e3133ddbd213a493d7b5bab3`. The reviewer performed source/log reads and wrote only this report; no build, test, native launch, screenshot or workload was performed by the reviewer. No additional repository AGENTS.md or local SKILL.md was found in the inspected workspace locations. This is an independent scoped review, not a Standards Engine certification. + +## Reviewed source identity + +| File | SHA-256 | +| --- | --- | +| `src/bin/desktop/native_presentation_harness.rs` | `7ecb3487c24d57eb726e8212d1393bf855ca13125af53021bb0c6237dc056c6e` | +| `src/bin/desktop/viewport.rs` | `c79ef51daf9f0079f424f9d6d24343931d70d2f5d4fe233ee978931080aaa462` | +| `src/bin/rheon-desktop.rs` | `daf9b7e54d89decfbfbff8ae8dd7cb17cc4fbab3fd50f3323f442f02dd1c55b3` | + +The production diff is confined to test configuration: the new module and alternate main are `cfg(test)`; the original main body is unchanged behind `cfg(not(test))`. The viewport field, default initializer, Appearance guard and inert-state query are also `cfg(test)`. No Cargo dependency, normal CLI/environment switch, runtime request, archive or physics implementation is added. + +## Complete admitted helper boundary + +The only admitted ordinary probe is `native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless`. Its complete construction, paint, input and drop path was inspected before execution admission. + +`fixture` supplies literal Request metadata: size 2, one requested step, two distinct method identifiers, one thread, 1 m domain, 0.25 s timestamp spacing, edge-2 preview and 16 MiB planning budget. Material, emitter, mesh, configured/saved intent, receipts, gates and prior IDs are absent. The recording path is an unused sentinel string. `validate_request` checks limits, geometry and buffer/history arithmetic; it does not construct a Simulation. `BufferPlan::for_grid` computes checked storage sizes. The preview constructor reserves bounded in-memory frame slots and creates default readerless Inspectors. + +Publication directly constructs four PreviewFrames (two tracks, two ordered frames each), with eight supplied density bytes per frame, steps 0/1, times 0/0.25, zero unmeasured calculation duration and no reports/material. It calls in-memory `LiveHistory::publish`, not capture, timing or numerical sampling helpers. The explicit paused track/index-zero and `follow_live=false` initialization avoids following the last-published track. + +`App::default` and its default controls construct values, empty owners and strings, with an available-CPU query; worker and scheduler are absent. The harness never composes `App::update`, sidebar, run controls, polling, dispatch, archive/saved views or model loading. `selected_frame` only reads the in-memory history here. Its `ensure_timestep` reaches a default Inspector with no reader: `select_timestep` fails before the reader-selection closure and any archive I/O. That absent-recording feedback is not evidence of archive integrity or recording completeness. + +The wrapper composes actual scene context and timeline helpers, then re-fetches the frame after timeline controls, matching production ordering. The Appearance renderer scans only the eight literal density cells. No collision mesh, emitter or worker voxel input exists. Draft force glyphs are presentation/validation only; `editable_scene=false` disables handles. No numerical lab owner is constructed. The entire viewport is disabled, and a test-only post-tab guard forces Appearance before any Particles/Obstacle/Transport controls or rendering branch, even if a tab interaction were unexpectedly reported. `presentation_is_inert` checks Appearance plus absent engineering/transport owners after every paint. + +Pinned egui 0.33.3 response/UI source was readable from the workspace tool cache: disabled UI does not produce hovered/clicked interactions; the camera input helper exits before processing keys/wheel unless hovered or dragged. The earlier plan review's denied `/root/.cargo` read was not retried; this is a different accessible dependency location. The unconditional mode guard additionally removes reliance on disabled tab semantics for lab dispatch. Existing SceneUi/handle input paths are disabled or noneditable. No app recording/export/screenshot/persistence hook is composed. The enabled eframe dependency features omit persistence, and `persist_window=false` is explicit. + +Normal ownership drops only in-memory data. `App::drop` cancels/joins only when a worker exists; this wrapper never creates one. Readerless Inspectors introduce no reader teardown or file cleanup. Native window/display initialization is outside this application-data I/O assertion. + +## Presentation fidelity and lifecycle + +The persistent banner says “Synthetic presentation data · no worker or physics”; a second visible note identifies supplied timestamps, unmeasured calculation duration and absence of archive/scientific results. These qualifications are essential because reused timeline captions include simulation/calculation wording. The banner must remain present in all native captures. + +The harness mirrors dark style, 8×7 spacing, 280 px sidebar reservation and the narrow/open-Scene decision, without composing setup actions. It uses one canonical App/SceneUi/timeline owner. Draft hides playback, pauses it and preserves the retained index; Retained uses the existing timeline controls and timestamp selection. It is a composed presentation fixture, not normal production startup/sidebar or runtime parity evidence. The disabled canvas deliberately cannot validate ordinary camera, picking or handle gestures. + +The fixed initial native window is 1250×820, with minimum 600×430; operators may resize it normally. There is no size environment selector. The Close control requests ordinary window closure. The single 300-second monotonic deadline requests Close from repaint; it is cooperative, not a hard process/GPU/teardown bound. Native validator process supervision remains required. Interaction does not extend that deadline. + +## Build gate and evidence receipt + +The ordinary compiler-generated cargo test main enumerates the one pure harness probe and no native test entry. The standalone recipe is `cargo rustc --locked --offline --features desktop --bin rheon-desktop -- --cfg test`, without `--test`; it compiles the separate main-thread eframe entry. Other test-only modules may compile, but no test runner or unrelated test is invoked by that main. No native binary execution is admitted in this environment. + +Read-only log receipt: `/tmp/rheon-ui-presentation-list4.log` enumerates 190 tests and zero benchmarks. `/tmp/rheon-ui-presentation-exact2.log` names only the fully qualified admitted probe and records one pass, zero failures, 189 filtered out, 0.06 s. This is the final post-guard probe; the earlier exact1 pass preceded that guard and is historical evidence only. The final source hashes above match the inspected post-guard source. + +The probe checks banner presence at 1250×820, 900×650 and 600×433.33334 logical sizes; attempts pointer press/release on all three disabled lab tabs; verifies inert owners, selected literal timestamps, Draft pause/index retention, unchanged deadline and relevant frozen Request metadata. It does not run a native entry. It does not establish full label clipping, real DPI/font/GPU behavior, screen-reader acceptance or native timeline interaction. + +The final standalone build2 log records success in 7.80 s. Its 258 unused-code/import warnings arise from the standalone `cfg(test)` compilation and are not a strict test-configuration Clippy pass; no lint suppression was added. The ordinary production build2 restored the normal target in 15.73 s; strict production Clippy2 passed in 2.81 s and locked check2 in 1.28 s. Final fmt2/diff2 logs are empty and the parent reports successful exits. No executable was run by the reviewer or reported run in these gates. + +The independently hashed final standalone artifact is `/workspace/scratch/rheon-ui-presentation/rheon-ui-synthetic-presentation-testcfg-guarded-425a986-increment.bin`: 235,185,520 bytes, SHA-256 `040715719f4e34e04951c8ad428024cbdbd219d7b65ac95d64d54e3e6e8d5cbd`. Its `guarded-standalone-binary-seal.json` has SHA-256 `fd84034c3e0ed1e8895aa0dbbafaaf16571ec1d375d7a2840d218bac5c679d6c`, identifies base `425a9869fa3f36b7e3133ddbd213a493d7b5bab3`, `build_cfg=test`, `rustc_test_harness=false`, `executed=false`, and agrees with the actual file's hash/size. The uniquely named copy survives the normal target restoration. The earlier successful standalone artifact preceded the final guard and is superseded for transfer. The existing native validator must execute only the final sealed standalone artifact, under its own authorization. Source hashes above bind the reviewed local increment, not an invented committed revision. + +Final packet receipt: `evidence/synthetic-presentation-20261020/manifest.json`, SHA-256 `72df6b803271253cdaede05edfcf48cf9b6318a2fc61946a61abdca18b1e09c8`, was independently checked against all three current source files, all nine copied/raw log pairs (hash and size), and the guarded standalone artifact and seal JSON. Eight logs cover the current accepted list/exact/build/restore/lint/check/format/diff gates; the ninth preserves the initial compilation failure. That failure records unavailable `egui_winit`/`with_any_thread` and forbidden App struct-update moves under Drop. The final code removes the cross-thread native-test design and uses default-then-field assignment; the accepted compile/probe logs supersede that failed attempt without deleting it. The list contains exactly one harness test entry, once, and the exact result names only that probe. The manifest marks native execution false; no native execution is claimed by this receipt. + +The read-only handoff review of `reports/native-synthetic-presentation-handoff.md` agrees with the inspected entry point and final artifact. It distinguishes standalone compilation from exact test execution, immediate unique sealing from ordinary-target restoration, and separately authorized native execution/process supervision. It retains synthetic annotations, cooperative closure and the viewport/physics/archive limitations. Native builds in another environment must seal their own executable and source identity; this local binary hash is not a reproducibility guarantee. + +## Remaining acceptance limits + +- The plan requested attempted canvas/key inputs; the current probe attempts disabled tab pointers only. Camera/key containment is source-reviewed, not empirically exercised here. Do not report canvas/wheel/key interaction coverage. +- Native Play/Pause, speed, track popup, slider, Draft↔Retained, resize/scale, banner visibility and Close behavior await the existing validator. Quality-85 JPEG delivery and native process closure are not established by this report. +- Normal production startup/sidebar integration, viewport gestures, force provenance, actual runtime/recording/archive parity, worker progress/cancellation and scientific correctness remain outside this harness. + +No material source blocker remains for the scoped synthetic harness and the one admitted pure probe. These remaining gates limit evidence claims; they do not authorize broader tests or workloads. diff --git a/src/bin/desktop/native_presentation_harness.rs b/src/bin/desktop/native_presentation_harness.rs new file mode 100644 index 00000000..8094200a --- /dev/null +++ b/src/bin/desktop/native_presentation_harness.rs @@ -0,0 +1,308 @@ +//! Test-only synthetic presentation. Never compiled into the production launch. +use super::*; +const LABEL: &str = "Synthetic presentation data · no worker or physics"; +const TIME_NOTE: &str = + "Supplied timestamps; calculation duration is unmeasured. No archive or scientific result."; +fn style(ctx: &egui::Context) { + let mut style = (*ctx.style()).clone(); + style.visuals = egui::Visuals::dark(); + style.visuals.selection.bg_fill = egui::Color32::from_rgb(22, 98, 138); + style.spacing.item_spacing = egui::vec2(8.0, 7.0); + ctx.set_style(style); +} +fn fixture() -> App { + let request = Request { + collision_mesh: None, + size: 2, + steps: 1, + tight: false, + threads: 1, + methods: PressureImplementation::ALL.to_vec(), + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Edge(2), + domain_side: 1.0, + requested_dt: 0.25, + material: None, + material_color: [0; 3], + recording_limits: rheon::recording::RecordingLimits { + max_database_bytes: 65536, + max_record_bytes: 16384, + max_records: 2, + }, + recording_directory: "synthetic-unused-no-output".into(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + }; + validate_request(&request).expect("literal metadata is legal; no solver constructed"); + let history = request + .preview_history() + .expect("bounded in-memory display history"); + for (track, method) in request.methods.iter().enumerate() { + for (step, time) in [0.0, 0.25].into_iter().enumerate() { + let density = match (track, step) { + (0, 0) => vec![0; 8], + (0, 1) => vec![255, 0, 0, 0, 0, 0, 0, 0], + (1, 0) => vec![0, 0, 0, 0, 0, 0, 0, 128], + _ => vec![0, 128, 128, 0, 0, 0, 0, 0], + }; + history + .publish( + track, + Arc::new(PreviewFrame { + method: *method, + step, + time, + seconds: 0.0, + edge: 2, + nonzero_tracer_cells: density.iter().filter(|&&v| v != 0).count(), + density, + report: None, + particle_report: None, + material_color: None, + }), + ) + .unwrap(); + } + } + let mut app = App::default(); + app.history = Some(history); + app.running_request = Some(request); + app.follow_live = false; + app.track = 0; + app.timeline_step = 0; + app.playing = false; + app.play_time = 0.0; + app.status = LABEL.into(); + app.scene.begin_run(false); + app +} +struct Presentation { + app: App, + deadline: Instant, +} +impl Presentation { + fn new() -> Self { + Self { + app: fixture(), + deadline: Instant::now() + Duration::from_secs(300), + } + } + fn paint(&mut self, ctx: &egui::Context) -> Option { + assert!(self.app.worker.is_none()); + if Instant::now() >= self.deadline { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + egui::TopBottomPanel::top("synthetic-label").show(ctx, |ui| { + ui.horizontal_wrapped(|ui| { + ui.strong(LABEL); + if ui.button("Close presentation").clicked() { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + ui.small(TIME_NOTE); + }); + // Match production sidebar reservation without composing any run controls. + if !(self.app.scene.open && ctx.content_rect().width() < 1100.0) { + egui::SidePanel::left("synthetic-settings-geometry") + .exact_width(280.0) + .show(ctx, |ui| { + ui.label("Frozen synthetic Request"); + ui.label("2 tracks · 2 literal frames each"); + ui.label("1 m · 2³ display voxels"); + ui.label("No worker, recorder, model or solver"); + }); + } + if self.app.scene.context == scene_ui::SceneContext::Draft { + self.app.playing = false; + } + if self.app.playing { + self.app.play_time += + self.app.playback_speed * f64::from(ctx.input(|i| i.stable_dt.min(0.1))); + } + let previous = self.app.scene.context; + self.app.scene.show(ctx, true, false, None); + if previous != self.app.scene.context { + self.app.playing = false; + } + let retained = self.app.scene.context == scene_ui::SceneContext::Retained; + let (count, frame) = if retained { + self.app.selected_frame() + } else { + (0, None) + }; + let timeline = self.app.scene_timeline_ui(ctx, count, frame.as_deref()); + let frame = if retained { + self.app.selected_frame().1 + } else { + None + }; + self.app.viewport.domain_side = 1.0; + self.app.viewport.authored_forces_pending = + !retained && !self.app.scene.draft().objects.is_empty(); + // Every frame resets to the fresh default Appearance owner. Disabled UI + // prevents user dispatch into numerical labs; no production App::update. + self.app.viewport.presentation_only_appearance(); + egui::CentralPanel::default() + .frame(egui::Frame::NONE) + .show(ctx, |ui| { + ui.add_enabled_ui(false, |ui| { + self.app.viewport.show_scene( + ui, + viewport::ViewInputs { + frame, + emitter: None, + workers: &[], + grid_edge: 2, + inspector: None, + scene: Some(&mut self.app.scene), + editable_scene: false, + }, + ) + }); + }); + assert!(self.app.viewport.presentation_is_inert()); + ctx.request_repaint_after(Duration::from_millis(if self.app.playing { + 16 + } else { + 100 + })); + timeline + } +} +impl eframe::App for Presentation { + fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) { + self.paint(ctx); + } +} +#[test] +fn synthetic_retained_history_timeline_and_disabled_modes_are_workerless() { + let mut h = Presentation::new(); + let original = h.app.running_request.as_ref().unwrap().clone(); + let deadline = h.deadline; + for (width, height) in [(1250.0, 820.0), (900.0, 650.0), (600.0, 433.33334)] { + let ctx = egui::Context::default(); + style(&ctx); + for _ in 0..2 { + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, height), + )), + ..Default::default() + }, + |ctx| { + assert!(h.paint(ctx).is_some()); + }, + ); + assert!( + output + .shapes + .iter() + .any(|s| matches!(&s.shape,egui::Shape::Text(t)if t.galley.text()==LABEL)) + ); + } + let mut tabs = vec![]; + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, height), + )), + ..Default::default() + }, + |ctx| { + h.paint(ctx); + }, + ); + for s in output.shapes { + if let egui::Shape::Text(t) = s.shape + && ["Particles", "Obstacle lab", "Transport session"].contains(&t.galley.text()) + { + tabs.push(t.pos + t.galley.size() * 0.5); + } + } + assert_eq!(tabs.len(), 3); + for p in tabs { + for pressed in [true, false] { + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(width, height), + )), + events: vec![ + egui::Event::PointerMoved(p), + egui::Event::PointerButton { + pos: p, + button: egui::PointerButton::Primary, + pressed, + modifiers: egui::Modifiers::NONE, + }, + ], + ..Default::default() + }, + |ctx| { + h.paint(ctx); + }, + ); + assert!(h.app.viewport.presentation_is_inert()); + } + } + } + let history = h.app.history.as_ref().unwrap(); + assert_eq!(history.preview_edge, 2); + assert_eq!(history.view(0, 0, false).unwrap().1.unwrap().time, 0.0); + assert_eq!(history.view(1, 1, false).unwrap().1.unwrap().time, 0.25); + h.app.track = 1; + h.app.timeline_step = 1; + h.app.follow_live = false; + let ctx = egui::Context::default(); + style(&ctx); + h.app.scene.context = scene_ui::SceneContext::Draft; + h.app.playing = true; + let _ = ctx.run(Default::default(), |ctx| assert!(h.paint(ctx).is_none())); + assert!(!h.app.playing); + assert_eq!(h.app.timeline_step, 1); + h.app.scene.context = scene_ui::SceneContext::Retained; + let _ = ctx.run(Default::default(), |ctx| assert!(h.paint(ctx).is_some())); + assert_eq!(h.app.track, 1); + assert_eq!(h.app.timeline_step, 1); + assert_eq!(h.deadline, deadline); + assert!(h.app.worker.is_none()); + let request = h.app.running_request.as_ref().unwrap(); + assert_eq!(request.size, original.size); + assert_eq!(request.methods, original.methods); + assert_eq!( + request.requested_dt.to_bits(), + original.requested_dt.to_bits() + ); + assert!( + request.collision_mesh.is_none() && request.material.is_none() && request.emitter.is_none() + ); +} +// Compile with cargo rustc --bin rheon-desktop -- --cfg test (no --test). +// This entry is the main-thread standalone test-config build, never an exact +// unit-test execution or the normal production binary launch. +pub fn run_native_presentation() -> eframe::Result { + eframe::run_native( + "Rheon · synthetic presentation · no physics", + eframe::NativeOptions { + persist_window: false, + viewport: egui::ViewportBuilder::default() + .with_inner_size([1250.0, 820.0]) + .with_min_inner_size([600.0, 430.0]), + ..Default::default() + }, + Box::new(|cc| { + style(&cc.egui_ctx); + Ok(Box::new(Presentation::new())) + }), + ) +} diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index d4311bd4..0a697065 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -61,6 +61,8 @@ fn view_mode_tabs(ui: &mut egui::Ui, current: &mut ViewMode, blocked: bool) -> [ }) } pub struct Viewport { + #[cfg(test)] + presentation_only: bool, scene_handles: super::scene_handles::SceneHandles, /// Draft authored fields have no runtime/preview integration yet. pub authored_forces_pending: bool, @@ -83,6 +85,8 @@ pub struct Viewport { impl Default for Viewport { fn default() -> Self { Self { + #[cfg(test)] + presentation_only: false, scene_handles: Default::default(), authored_forces_pending: false, camera: OrbitCamera::default(), @@ -116,6 +120,16 @@ pub struct ViewInputs<'a> { pub editable_scene: bool, } impl Viewport { + #[cfg(test)] + pub(super) fn presentation_only_appearance(&mut self) { + self.presentation_only = true; + self.mode = ViewMode::Appearance; + } + #[cfg(test)] + pub(super) fn presentation_is_inert(&self) -> bool { + self.mode == ViewMode::Appearance && self.engineering.is_none() && self.transport.is_none() + } + #[cfg(test)] pub fn show( &mut self, @@ -168,6 +182,10 @@ impl Viewport { } } }); + #[cfg(test)] + if self.presentation_only { + self.mode = ViewMode::Appearance; + } if self.mode == ViewMode::Appearance && let Some(scene) = scene.as_deref() { diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index ce04a4ef..621779d2 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -52,6 +52,9 @@ pub mod run_sets; pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; +#[cfg(test)] +#[path = "desktop/native_presentation_harness.rs"] +mod native_presentation_harness; #[path = "desktop/scene_handles.rs"] mod scene_handles; #[path = "desktop/scene_ui.rs"] @@ -2543,6 +2546,7 @@ impl Drop for App { } } } +#[cfg(not(test))] fn main() -> eframe::Result { eframe::run_native( "Rheon / Live 3D", @@ -3629,3 +3633,8 @@ mod tests { assert!(app.status.starts_with("Cancelled")); } } + +#[cfg(test)] +fn main() -> eframe::Result { + native_presentation_harness::run_native_presentation() +} From 5eab3ac4b6d4005ae77b145b3d970b9692debd4c Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:09:51 +0000 Subject: [PATCH 14/25] Wire gated desktop force snapshots through dispatch and recording --- .../force-custody-20261021/.gitattributes | 2 + .../force-custody-20261021/manifest.json | 253 +++++++++++ .../rheon-ui-t5-build1.log | 2 + .../rheon-ui-t5-check1.log | 33 ++ .../rheon-ui-t5-check2.log | 17 + .../rheon-ui-t5-check3.log | 2 + .../rheon-ui-t5-clippy1.log | 2 + .../rheon-ui-t5-diff1.log | 0 .../rheon-ui-t5-exact1.log | 8 + .../rheon-ui-t5-exact2.log | 8 + .../rheon-ui-t5-exact3.log | 8 + .../rheon-ui-t5-exact4.log | 8 + .../rheon-ui-t5-exact5.log | 8 + .../rheon-ui-t5-exact6.log | 8 + .../rheon-ui-t5-exact7.log | 8 + .../rheon-ui-t5-fmt1.log | 0 .../rheon-ui-t5-list1.log | 195 +++++++++ .../rheon-ui-t5-list2.log | 36 ++ .../rheon-ui-t5-list3.log | 201 +++++++++ .../rheon-ui-t5-list4.log | 202 +++++++++ .../rheon-ui-t5-plan1.log | 1 + .../rheon-ui-t5-plan2.log | 1 + .../rheon-ui-t5-regression1.log | 8 + .../rheon-ui-t5-regression2.log | 8 + .../rheon-ui-t5-regression3.log | 8 + .../rheon-ui-t5-regression4.log | 8 + .../rheon-ui-t5-regression5.log | 8 + .../rheon-ui-t5-regression6.log | 8 + docs/plans/ui-refinement/execution-ledger.md | 29 ++ docs/plans/ui-refinement/plan.md | 68 +-- .../reports/runtime-force-checkpoint.md | 93 ++++ ...untime-force-integration-implementation.md | 74 ++++ .../runtime-force-integration-plan-review.md | 121 ++++++ ...runtime-force-integration-source-review.md | 90 ++++ ...untime-force-validation-contract-review.md | 142 ++++++ .../runtime-force-validation-contract.md | 123 ++++++ src/bin/desktop/carrier_execution.rs | 23 + src/bin/desktop/comparison.rs | 1 + src/bin/desktop/configured_worker_tests.rs | 3 + src/bin/desktop/force_runtime_ui_tests.rs | 407 ++++++++++++++++++ src/bin/desktop/inspector.rs | 157 ++++++- .../desktop/native_presentation_harness.rs | 13 +- src/bin/desktop/run_sets.rs | 131 +++++- src/bin/desktop/run_sets/source.rs | 1 + src/bin/desktop/scene_handles.rs | 2 +- src/bin/desktop/scene_ui.rs | 51 ++- src/bin/desktop/scene_view.rs | 7 +- src/bin/desktop/setup_ui_tests.rs | 9 + src/bin/desktop/timeline_ui_tests.rs | 3 + src/bin/rheon-desktop.rs | 252 +++++++++-- 50 files changed, 2733 insertions(+), 118 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-build1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check2.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check3.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-clippy1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-diff1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact2.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact3.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact4.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact5.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact6.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact7.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-fmt1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list2.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list3.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list4.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan2.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression1.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression2.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression3.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression4.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression5.log create mode 100644 docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression6.log create mode 100644 docs/plans/ui-refinement/reports/runtime-force-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-integration-implementation.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-integration-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-integration-source-review.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-validation-contract-review.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-validation-contract.md create mode 100644 src/bin/desktop/force_runtime_ui_tests.rs diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/.gitattributes b/docs/plans/ui-refinement/evidence/force-custody-20261021/.gitattributes new file mode 100644 index 00000000..fe0e2fd6 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/.gitattributes @@ -0,0 +1,2 @@ +# Preserve captured libtest bytes, including intentional trailing blank lines. +*.log binary diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/manifest.json b/docs/plans/ui-refinement/evidence/force-custody-20261021/manifest.json new file mode 100644 index 00000000..9299632d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/manifest.json @@ -0,0 +1,253 @@ +{ + "base_commit": "8148fa239a60502a1245d5a69d0eeb8ceb905c16", + "backend_canonical_ancestor": "43ec505218c43594641ecbb80873bec8c88d7d2c", + "backend_fetched_docs_only_tip": "a4326a78a747bc868874fd16e434d780fd5a6d35", + "list_count": 197, + "exact_pure_tests_passed": 13, + "numerical_sqlite_native_execution": false, + "production_force_gate": "closed", + "sources": [ + { + "file": "src/bin/desktop/carrier_execution.rs", + "bytes": 8697, + "sha256": "c4455879373fc71eaaf398fdaa230c4d0b115dc7ff0a9da6356afeb24cba23b2" + }, + { + "file": "src/bin/desktop/comparison.rs", + "bytes": 46042, + "sha256": "27ad4b88db848430a68f5f9fae796fa10eab1df26af9dbae73386e55c9b5c74f" + }, + { + "file": "src/bin/desktop/configured_worker_tests.rs", + "bytes": 23533, + "sha256": "dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016" + }, + { + "file": "src/bin/desktop/inspector.rs", + "bytes": 46546, + "sha256": "aad6760e94582d6512c32387609c3c2874e92728c93c3c7d2089cbf3bc6ee62c" + }, + { + "file": "src/bin/desktop/native_presentation_harness.rs", + "bytes": 11577, + "sha256": "38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6" + }, + { + "file": "src/bin/desktop/run_sets.rs", + "bytes": 35607, + "sha256": "b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0" + }, + { + "file": "src/bin/desktop/run_sets/source.rs", + "bytes": 9660, + "sha256": "39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf" + }, + { + "file": "src/bin/desktop/scene_handles.rs", + "bytes": 35097, + "sha256": "4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22" + }, + { + "file": "src/bin/desktop/scene_ui.rs", + "bytes": 38145, + "sha256": "cc866e716876dfee8c9245c2473426c7c3253b2b0787b49b907202a6e1402557" + }, + { + "file": "src/bin/desktop/scene_view.rs", + "bytes": 37630, + "sha256": "d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f" + }, + { + "file": "src/bin/desktop/setup_ui_tests.rs", + "bytes": 32765, + "sha256": "db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900" + }, + { + "file": "src/bin/desktop/timeline_ui_tests.rs", + "bytes": 15811, + "sha256": "915c565a2be34618a4be8ad801e215a2abe15a7eaf801fb2e5353bf632bdad54" + }, + { + "file": "src/bin/rheon-desktop.rs", + "bytes": 163745, + "sha256": "0cc6bd651a60b2d09abc77bd49515a114d993faa842acebf19e3955ad9b7a62e" + }, + { + "file": "src/bin/desktop/force_runtime_ui_tests.rs", + "bytes": 15963, + "sha256": "0f8b88db0553cf079c0f78a2e41766fcb6160f6dda1a3d117f1ce93fe17b91f5" + } + ], + "logs": [ + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-build1.log", + "bytes": 131, + "sha256": "152118fd1ff777f1c5ea72c5402b3890bc162df01756d1668707a8d525004109", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check1.log", + "bytes": 1343, + "sha256": "57070a858fc15ba2b08995197d0d0e0220f0b800e669c9185b67f3be8b3e2d20", + "classification": "preserved-failed-or-intermediate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check2.log", + "bytes": 796, + "sha256": "c1193412969bf54fc1ffa2f4e46bbf933cafff6d96ed6d6e2b97ef4d4e80c06d", + "classification": "preserved-failed-or-intermediate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check3.log", + "bytes": 130, + "sha256": "3a80f27070236bb0efdf91e3f9f896ff9f515cf026e348610f26e1124ada4a75", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-clippy1.log", + "bytes": 130, + "sha256": "6705a8f70c099ae688d5ee40b888d9e7e43dcda0ff8534a02e06f4a40b126f9a", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-diff1.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact1.log", + "bytes": 399, + "sha256": "a06355331dcc77cbdcae6f6da355305ad9861c1dabd62ed0e99448fdb8b6fc69", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact2.log", + "bytes": 418, + "sha256": "ba2351a20f74c4cd6bce9446597b7428e8c8e30b489a98ac413239360b633e66", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact3.log", + "bytes": 420, + "sha256": "f11fc3eb73e76cf36dbd3c4864465342e1eab385fc1b740d63ddb6c0e5acb265", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact4.log", + "bytes": 417, + "sha256": "9b3bc10f5f4e74c0db2a9061ded6552aa8039fb471a985dfb0e72f7dd2fcbccd", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact5.log", + "bytes": 430, + "sha256": "827d142c855962ca42c717d180e5d64ca2229b67bf237a28f0fa80fa7b6e2577", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact6.log", + "bytes": 419, + "sha256": "519ab2d32c9056783d81ade6cf87b17abd456f47d113d24497abac15dea923ba", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact7.log", + "bytes": 424, + "sha256": "5081b4085c8ccfcbc7200bcda773bac25da864f27d2bbeea8e9ee065a66d8367", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-fmt1.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list1.log", + "bytes": 20089, + "sha256": "dd356787f3f1ef6d8cbc74f00293a5fc6fe7a6ae14da5584b67dc20a51b05d3f", + "classification": "preserved-failed-or-intermediate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list2.log", + "bytes": 1757, + "sha256": "47b2090db593ec256a1697d37cdc184f9c7cada4c4b9cf8b88c52e9aacafef89", + "classification": "preserved-failed-or-intermediate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list3.log", + "bytes": 20696, + "sha256": "82d060948c2234125eee8ae63135c03e3872169aed2f2d46ab02f201901730fd", + "classification": "preserved-failed-or-intermediate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list4.log", + "bytes": 20803, + "sha256": "23adbcb420f4dea3b1dc890e7580d9189c6b07dcdc0c38c92435b304ea57d87b", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan1.log", + "bytes": 83, + "sha256": "9430eecf97daf0303c2a715878bb41924c41c96eed26b07e21ee0ea44b38dd40", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression1.log", + "bytes": 421, + "sha256": "79bcfcb64cf46aefd737b81c3e4270bac322280e688bf718089a07f1004f9293", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression2.log", + "bytes": 399, + "sha256": "6b5ca967674c46e623e42781070e0a530b07045122f544623c6fe13af8c9c715", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression3.log", + "bytes": 411, + "sha256": "04a5e6db91b7e1f837aad752cba288aca98588c3562680e20ab1821aa9658ec1", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression4.log", + "bytes": 402, + "sha256": "e2e0e3c8881b4c353e6bbd39ce453b74f9a8468439017c4cf741fde05f6ecb5e", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression5.log", + "bytes": 419, + "sha256": "fccb202e93ab3ff32ecfdb38f9e79047967925fb63885b2b1d5cf214484b406a", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression6.log", + "bytes": 410, + "sha256": "463dfe536be01b791969d934dc2b332de4b59d3fc0f3c35b5f5f4590d2b35dca", + "classification": "final-gate" + }, + { + "file": "docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan2.log", + "bytes": 76, + "sha256": "0e551a02a144eae088f3a9130613de9428d602f6f8099cdc7dc7760c4557d9cb", + "classification": "final-gate" + } + ], + "binaries": [ + { + "path": "/workspace/.rheon-tools/target/debug/rheon-desktop", + "bytes": 284918080, + "sha256": "86e0bb942780d63d4f2ba1e15b067ff35b19a6821639ca74b7ca462ec674ee54", + "execution": "not-launched" + }, + { + "path": "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "bytes": 480161664, + "sha256": "d28b45199359f5cfe45b374386c7368685d68900bacd72694004039bd3318b16", + "execution": "13-exact-pure-tests-only" + } + ] +} diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-build1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-build1.log new file mode 100644 index 00000000..ab901135 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-build1.log @@ -0,0 +1,2 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 13.80s diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check1.log new file mode 100644 index 00000000..2cfdd09e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check1.log @@ -0,0 +1,33 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +warning: unused import: `SceneContext` + --> src/bin/desktop/scene_view.rs:4:16 + | +4 | scene_ui::{SceneContext, SceneUi}, + | ^^^^^^^^^^^^ + | + = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default + +error[E0004]: non-exhaustive patterns: `run_sets::SpecDifference::ForceMetadata` not covered + --> src/bin/desktop/comparison.rs:992:11 + | +992 | match d { + | ^ pattern `run_sets::SpecDifference::ForceMetadata` not covered + | +note: `run_sets::SpecDifference` defined here + --> src/bin/desktop/run_sets.rs:525:10 + | +525 | pub enum SpecDifference { + | ^^^^^^^^^^^^^^ +... +534 | ForceMetadata, + | ------------- not covered + = note: the matched value is of type `run_sets::SpecDifference` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +1000~ SpecDifference::RecordingBudget => "recording budget", +1001~ run_sets::SpecDifference::ForceMetadata => todo!(), + | + +For more information about this error, try `rustc --explain E0004`. +warning: `rheon` (bin "rheon-desktop") generated 1 warning +error: could not compile `rheon` (bin "rheon-desktop") due to 1 previous error; 1 warning emitted diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check2.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check2.log new file mode 100644 index 00000000..769e0863 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check2.log @@ -0,0 +1,17 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +warning: unused `std::result::Result` that must be used + --> src/bin/rheon-desktop.rs:2040:17 + | +2040 | / self.scene +2041 | | .bind_retained_fields(0, request.fields_for_track(0)); + | |_________________________________________________________________________^ + | + = note: this `Result` may be an `Err` variant, which should be handled + = note: `#[warn(unused_must_use)]` (part of `#[warn(unused)]`) on by default +help: use `let _ = ...` to ignore the resulting value + | +2040 | let _ = self.scene + | +++++++ + +warning: `rheon` (bin "rheon-desktop") generated 1 warning + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.68s diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check3.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check3.log new file mode 100644 index 00000000..f34d1be3 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-check3.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.43s diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-clippy1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-clippy1.log new file mode 100644 index 00000000..b10d5e87 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-clippy1.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.02s diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-diff1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-diff1.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact1.log new file mode 100644 index 00000000..00cf9870 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact1.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.23s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact2.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact2.log new file mode 100644 index 00000000..0adab817 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact3.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact3.log new file mode 100644 index 00000000..fea1a30d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact3.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact4.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact4.log new file mode 100644 index 00000000..d50481fa --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact4.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.23s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact5.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact5.log new file mode 100644 index 00000000..ae962db7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact5.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact6.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact6.log new file mode 100644 index 00000000..e4146101 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact6.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact7.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact7.log new file mode 100644 index 00000000..f2b8e00c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-exact7.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-fmt1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-fmt1.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list1.log new file mode 100644 index 00000000..e7acce08 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list1.log @@ -0,0 +1,195 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 41.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +190 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list2.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list2.log new file mode 100644 index 00000000..5d901e51 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list2.log @@ -0,0 +1,36 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error[E0502]: cannot borrow `*self.scene.context` as immutable because it is also borrowed as mutable + --> src/bin/rheon-desktop.rs:2691:32 + | +2679 | / viewport::ViewInputs { +2680 | | frame, +2681 | | emitter, +2682 | | workers: &workers, +... | +2689 | | scene: Some(&mut self.scene), + | | --------------- mutable borrow occurs here +2690 | | editable_scene: self.worker.is_none() +2691 | | && self.scene.context == scene_ui::SceneContext::Draft, + | | ^^^^^^^^^^^^^^^^^^ immutable borrow occurs here +2692 | | }, + | |_____________________- mutable borrow later used here + | +help: try adding a local storing this... + --> src/bin/rheon-desktop.rs:2691:32 + | +2691 | ... && self.scene.context == scene_ui::SceneContext::Draft, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ...and then using that local here + --> src/bin/rheon-desktop.rs:2679:21 + | +2679 | / viewport::ViewInputs { +2680 | | frame, +2681 | | emitter, +2682 | | workers: &workers, +... | +2691 | | && self.scene.context == scene_ui::SceneContext::Draft, +2692 | | }, + | |_____________________^ + +For more information about this error, try `rustc --explain E0502`. +error: could not compile `rheon` (bin "rheon-desktop" test) due to 1 previous error diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list3.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list3.log new file mode 100644 index 00000000..5b301bc3 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list3.log @@ -0,0 +1,201 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 15.33s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +196 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list4.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list4.log new file mode 100644 index 00000000..aa028c30 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-list4.log @@ -0,0 +1,202 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 7.40s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +197 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan1.log new file mode 100644 index 00000000..553ba49d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan1.log @@ -0,0 +1 @@ +Pinned standards structural plan contract passed; no standards-engine certificate. diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan2.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan2.log new file mode 100644 index 00000000..1d9ebe0d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-plan2.log @@ -0,0 +1 @@ +Final pinned structural plan check passed; no standards-engine certificate. diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression1.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression1.log new file mode 100644 index 00000000..b44b56c9 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression1.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.21s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.08s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression2.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression2.log new file mode 100644 index 00000000..47e1fe1f --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.10s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression3.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression3.log new file mode 100644 index 00000000..cdc9c988 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression3.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression4.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression4.log new file mode 100644 index 00000000..085d529f --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression4.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression5.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression5.log new file mode 100644 index 00000000..dd255272 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression5.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.02s + diff --git a/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression6.log b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression6.log new file mode 100644 index 00000000..a8099ffd --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-custody-20261021/rheon-ui-t5-regression6.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 196 filtered out; finished in 0.03s + diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 5e68a2cd..aca4f64e 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -456,3 +456,32 @@ is in reports/runtime-force-integration-admission.md; coordination is pending be Inspector edits. No numerical workload or force launch was enabled. Independent pure versioned intent-codec work may follow after that interface/dependency agreement; preserve legacy global manifest/schema/hash contracts rather than blind version bump. + +## T5 desktop force custody source —2026-10-21 + +Parent extended desktop ownership to minimal Inspector begin/record wrappers. On +isolated PR146 branch after8148fa2, implemented canonical force snapshot dispatch +and recording for native carrier and particle material, exact per-track immutable +Request binding, v2 native intent/hash/load/rerun, and read-only retained snapshot +projection. Production force admission remains false before worker/output; saved +reruns and unsupported owner paths refuse explicitly. No core/solver/recording +module changed. Canonical43ec505 core is unchanged in fetched a4326a78; backend +admission documents are not copied/refreshed or extended to this UI closure. + +Independent design/source reviews required/repaired one-render-frame retained +composition and independent v2 byte/hash goldens. Thirteen individually exact pure/ +Context tests passed vs197 list; no numerical/model/SQLite/file fixture/native run. +Locked production check1.43s, strict Clippy4.02s, build13.80s, fmt/diff and pinned +structural plan check passed.14source seals/26logs, including intermediate compile/ +list failures and warnings, are preserved in force-custody-20261021 manifest SHA + b73005e773cc7c15eb820509104cf3025c7aa2b565169cefc58cc2b774bc1906. +See reports/runtime-force-checkpoint.md and independent source review for exact +binary/source seals, names, meaning and limits; elapsed compiler timings are not +workload resource measurements. Original M2a source hash remains0937b5e2…dc102. + +Future one-step carrier/material cases have an independently reviewed bounded +contract, but ignored test bodies are not implemented/enumerated and no execution +is admitted. Production launch gate stays; runtime/archive parity, genuine Cancel, +long-status/accessibility/full M1 and T5 native acceptance are unqualified. No native +image/Library claim; existing8148fa2 validator continues independently. Preserve +all earlier captures, seals, branches, incident evidence and disposable outputs. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 20fece6a..e764005b 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T4 source published; synthetic retained presentation handoff ready -**Next slice:** T5 interface coordination and pure versioned frozen-intent codecs +**Current phase:** T5 desktop custody source implemented; bounded validation pending +**Next slice:** independently reviewed bounded native-carrier/material runtime and archive probes **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -16,7 +16,10 @@ [Backend receipt](reports/backend-integration-receipt.md) · [Current authoring admission](reports/scene-authoring-admission.md) · [Authoring source review](reports/scene-authoring-source-review.md) · -[Native authoring handoff](reports/native-scene-authoring-handoff.md) +[Native authoring handoff](reports/native-scene-authoring-handoff.md) · +[T5 interface and implementation](reports/runtime-force-integration-implementation.md) · +[T5 independent review](reports/runtime-force-integration-source-review.md) · +[T5 bounded validation contract](reports/runtime-force-validation-contract.md) ## Objective @@ -72,16 +75,19 @@ No competing force physics, backend-specific UI approximations, solver/model/ scientific execution, unadmitted archive migration, worker/session ownership change, training, L1/MMS work, dependency/framework rewrite, merge, force push or cleanup. Live cancellation stays parked on missing existing containment; no provisioning. -Canonical domain source is now received and sealed. No fabricated runtime support -while desktop dispatch/frozen intent/load/rerun integration remains pending. +Canonical domain source is received and sealed. Desktop dispatch/frozen intent/load/ +rerun wiring is source-complete but force-enabled launches remain gated until the +changed desktop closure receives separately authorized bounded runtime evidence. ## Dependency strategy and ownership See the binding [integration contract](reports/scene-integration-contract.md). UI owns desktop composition/rendering/input/selection; UI allocates canonical SceneObjectId values across models/forces; force owner owns the type, physical units/support/frame/time semantics, validation/freeze and both -solver adapters. The exact backend API/source at1d44c842 is received and sealed; desktop runtime -integration remains pending. See the source receipt and independent review. See exact proposed runtime/custody +solver adapters. Canonical core source is integrated at43ec5052; fetched a4326a78 +changes only backend admission documents, which are not copied or refreshed here. +Desktop runtime validation remains pending. See the source receipt and independent +review. See exact runtime/custody file reservations in the contract; none is edited in T1. Force owner must not edit desktop UI files concurrently. UI must not edit force/solver implementations. Exchange a pinned API/patch and serially integrate only after source review. No speculative merges or duplicate physics. @@ -99,7 +105,7 @@ Inspector slice before any overlapping changes; T1 excludes Inspector entirely. | T3 | new scene_view.rs geometry/picking; viewport.rs aggregate-input composition and thin geometry outline; existing SceneUi selection and main routing | [Exact selection admission](reports/viewport-selection-admission.md), inert camera/geometry/UI tests; no lab initialization, then native | Bounded source complete; native pending | | T4 | desktop/scene_handles.rs, viewport exclusive gesture routing, scene_ui validated draft transaction; force-only (model placement deferred) | [Force-only handle admission](reports/force-handles-admission.md); same IDs/domain types; no physics; exact Escape rollback and terminal-frame ownership | Bounded source complete; native pending | | T4a | cfg(test) native_presentation_harness.rs, cfg-gated main and viewport containment; no production launch/Cargo change | [Workerless contract](reports/synthetic-retained-presentation-contract.md), one exact pure UI probe, standalone compile/sealed artifact; existing validator native only | Bounded source complete; native pending | -| T5 | main Request/run_material, carrier_execution.rs, desktop run_sets versioned intent/load/rerun; exact backend recording bridge and native fixes | Qualified canonical custody source + reviewed immutable intent/version compatibility + separately bounded integration evidence. Unsupported owner paths refuse | Planned | +| T5 | main Request/run_material, carrier_execution.rs, minimal Inspector recorder methods, run_sets.rs/source.rs plus exhaustive comparison label, scene retained projection, inert tests/fixture initializers | Canonical custody source + independently reviewed intent/version compatibility + separately bounded runtime/archive evidence. Unsupported owner paths refuse; no core/solver writes | Source implemented; validation gate retained | Stages execute serially under the UI owner; T2–T4 reuse some UI regions and are @@ -149,10 +155,10 @@ Generated logs/screenshots/transfer packets remain outside Git. | --- | --- | --- | --- | --- | | S1 | Empty idle timeline fits <=70 logical px in900/1250px windows with the preceding280px sidebar reserved; selected-frame captions remain present and may naturally wrap/grow; actual Play/Pause/step/Live input keeps prior transitions | integration / isolated egui painting/input / automated | pending | Four exact probes pass: idle48px versus106px, focused speed editing and native-track/literal-report layouts. Native/configured/popup-keyboard coverage remains open | | S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | Canonical authoring source and five exact UI probes pass; viewport picking/native remain pending | -| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent still pending | -| S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | Domain/manipulator stages not admitted yet | +| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent source implemented and gated; desktop runtime/archive validation pending | +| S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | T4 reviewed geometry/transaction source and six exact inert tests pass; full Viewport gesture/native evidence pending | | S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; prior426fd25 images do not cover this source | -| S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T1 focused gates reported in checkpoint; no scientific certificate | +| S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T5 thirteen exact inert tests, locked build/check, strict production Clippy and independent source review pass; no archive workload or scientific certificate | The former M1 A4/A5 and live cancellation remain unsatisfied. UI-5 has a reviewed isolated-form exact-bit repair, not native/global keyboard acceptance. Earlier @@ -199,12 +205,13 @@ Ordinary lint/layout failures stay within the current exact slice and retain log ## Blockers -Canonical force source is received/sealed in isolated branch; its arrival does not -implement scene UI or desktop runtime. Desktop dispatch/frozen intent/load/rerun -and unsupported owner paths still need staged integration and qualified evidence. -Right-scene -integration must reconcile exact Inspector work before overlap. Local native -display unavailable; JPEG delivery of older screenshots remains separately blocked. +Canonical force source and desktop scene/dispatch/frozen intent/load/rerun source +are implemented in the isolated owner branch. Production authored-force runs stay +gated pending separately bounded desktop runtime/archive execution; configured, +provider/session/transport/free-surface propagation remains explicitly unsupported. +Minimal Inspector recorder ownership is now assigned and source-reviewed. No core +recording/solver source changed here. Local native display is unavailable; the +existing validator owns exact8148fa2 native work and actual JPEG delivery separately. Live cancellation lacks required existing containment and stays parked. No mount, cgroup, permission, credential or network changes are authorized. @@ -217,12 +224,21 @@ No complete UI, force parity, native/accessibility, M1 or scientific acceptance claimed. Accept only exact staged source/test/native evidence once its objective criteria are met. Publish/merge only after separate coordination/authorization. -## T5 interface preflight - -[Runtime/archive boundary](reports/runtime-force-integration-admission.md) records -agreed Request/dispatch/run-set ownership and the newly identified minimal Inspector -recorder entry-point coordination. No Inspector, core or runtime T5 source changed. -Force launch refusal remains until corresponding runtime/archive custody is wired -and validated. Native425a986 handles and the separately labelled synthetic timeline -harness have distinct evidence; neither accepts worker cancellation or scientific -runtime parity. Preserve historical hashes and legacy spec versions exactly. +## T5 gated source checkpoint + +[Implementation admission](reports/runtime-force-integration-implementation.md) and +[independent source review](reports/runtime-force-integration-source-review.md) +record the assigned Request/dispatch/run-set/minimal Inspector write set. Native +carrier and particle material use canonical force adapters and recording wrappers; +legacy None keeps the old path. FrozenSpec v1 bytes/hashes are pinned, v2 requires +validated force metadata, manifest v1 supports independently valid mixed members. +Retained tree/properties and viewport share one render-frame track/document; +track intents paint consistently next frame and clear reused-ID selection. + +No numerical/SQLite/runtime or new native acceptance is claimed. Thirteen exact +pure/Context tests pass after list-only enumeration. Future two-case validation is +specified in [bounded contract](reports/runtime-force-validation-contract.md); +its ignored test bodies are not yet implemented or enumerated. Keep the production +force gate until separately admitted runtime/archive evidence qualifies this source. +Earlier native425a986/8148fa2 and synthetic harness artifacts retain their own exact +source/evidence; none accepts this changed desktop closure or genuine cancellation. diff --git a/docs/plans/ui-refinement/reports/runtime-force-checkpoint.md b/docs/plans/ui-refinement/reports/runtime-force-checkpoint.md new file mode 100644 index 00000000..0b19b69a --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-checkpoint.md @@ -0,0 +1,93 @@ +# Gated desktop force custody checkpoint + +The desktop now carries one immutable optional force snapshot per track through +native-carrier and particle-material dispatch, Inspector constructor/accepted +recording, saved native intent/load/rerun and retained scene presentation. It uses +canonical backend force/recording APIs, without changing core physics or adding a +second worker/session owner. All production force-enabled launches remain refused +pending bounded desktop runtime/archive validation. Unsupported configured/provider/ +session/transport/free-surface paths remain refused. + +## Compatibility and presentation + +Legacy None uses the existing execution/recording APIs. NativeCarrierSpec and +FrozenSpec v1 JSON bytes/hash are pinned by a literal golden. A force-bearing +FrozenSpec v2 contains the canonical ForceScene and a domain-separated length-delimited +hash over the old spec JSON and canonical exact-bit metadata. Manifest version1 +permits independently valid v1/v2 members. ForceMetadata differences distinguish +absence/empty, object order, signed zero, disabled objects, names and units. +Cold binding checks declaration/spec/run force digest; recorder preflight checks +expected fields before create. No automatic historical migration is introduced. + +Retained forces project from Request's selected-track snapshot, never the editable +draft. Selection clears when track/document changes even when IDs are reused. +Properties and outline are selectable/read-only; no retained manipulator handles. +Tree/properties and viewport use one cached render-frame track/frame; timeline track +intents apply visually together on the following paint. Legacy absent metadata +remains unknown, while a present empty document is explicitly known empty. + +## Focused evidence + +Final list-only enumeration:197 names, no execution. Thirteen individually named +`cargo test --locked --offline --features desktop --bin rheon-desktop NAME -- --exact --test-threads=1` +invocations each passed exactly one inspected inert test (196 filtered out): seven +new intent/binding/retained probes plus six changed-path regressions. Full helper +paths were independently read before selection; no Worker, numerical constructor/ +step, model, SQLite/filesystem fixture or cleanup occurs in those probes. Literal +report/preview data is presentation data, not calculated scientific evidence. + +The new probes pin v1 exact bytes/hash, independently constructed v2 literal bytes/ +hash, version/payload/tamper/unknown/duplicate/oversize rejection, order/None-empty +identity, pre-worker Request gates and saved-member binding, pre-create recorder +header association, immutable retained projection and same-frame coherence. +The regressions cover workerless synthetic presentation, SceneUi pointer/context, +retained index, geometry picking, direct handles cancellation/context and timeline +track/report layout. Direct handle tests do not establish full Viewport camera +fallthrough; no new native test was run here. + +Locked offline desktop check passed1.43s; strict production Clippy passed4.02s; +ordinary production build passed13.80s. Formatting, diff and pinned standards +structural plan checks passed; no standards-engine certificate is claimed. +Independent source review repaired a retained composition mismatch and added +independent v2 evidence. See the exact review/source seals and evidence manifest. +Earlier intermediate failed compile/list and warning receipts are preserved beside +accepted gates; they ran no tests. Staged raw libtest logs exposed their intentional +trailing blank lines to whitespace checks; the existing evidence-directory binary +attribute convention preserves those exact bytes, and the final staged check passes. +No production source changed after the checks. + +Evidence: `evidence/force-custody-20261021/manifest.json`,14source seals/26raw logs. +Manifest SHA256 `b73005e773cc7c15eb820509104cf3025c7aa2b565169cefc58cc2b774bc1906`. +Production binary284918080bytes SHA256 +`86e0bb942780d63d4f2ba1e15b067ff35b19a6821639ca74b7ca462ec674ee54`, never launched. +Test binary480161664bytes SHA256 +`d28b45199359f5cfe45b374386c7368685d68900bacd72694004039bd3318b16`, only those13exact +pure invocations. Mutable shared target paths are receipts at capture, not a +permanent artifact guarantee; rebuild/seal the exact published checkout for validation. + +## Dependency, rollback and remaining gates + +Incremental base8148fa239a60502a1245d5a69d0eeb8ceb905c16 on PR146's isolated branch. +Canonical core ancestor43ec505218c43594641ecbb80873bec8c88d7d2c. Backend +fetched a4326a78a747bc868874fd16e434d780fd5a6d35 was verified to change only its +admission review/inventory; neither is copied/refreshed for this new UI closure. +No core solver/recording source changed. Original uncommitted M2a settings source +remains SHA2560937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102. +Preserve every earlier branch/checkpoint/native artifact and incident/failure log. +Rollback only this distinct incremental commit, without migrating/deleting archives. + +The two proposed one-step runtime/archive cases are specified in +runtime-force-validation-contract.md; their ignored executable bodies are not yet +implemented/listed and no live execution is authorized here. Native carrier fixed-dt +and particle actual-dt association must be checked separately. Production gate, +SQLite/runtime parity, mixed-member rerun, active UI progress/Cancel/join, long status, +accessibility/usability and full M1 acceptance stay open. Live cancellation remains +parked on missing existing containment; no provisioning or alternate supervisor. + +The existing validator's exact8148fa2 work stays separate. Local DISPLAY and +WAYLAND_DISPLAY are absent; no native render/interaction, new JPEG, Library identity +or native repair/force acceptance is claimed. Sequence any T5 native session through +that owner after exact-source review. Idle force refusal can be inspected without +launching physics; testing a real force run/Cancel requires separately admitted +bounded execution. Actual screenshots must identify revision/state, distinguish +synthetic presentation, and use JPEG quality85; delivery requires confirmed identities. diff --git a/docs/plans/ui-refinement/reports/runtime-force-integration-implementation.md b/docs/plans/ui-refinement/reports/runtime-force-integration-implementation.md new file mode 100644 index 00000000..136b4861 --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-integration-implementation.md @@ -0,0 +1,74 @@ +# T5 desktop force custody implementation admission + +Base8148fa239a60502a1245d5a69d0eeb8ceb905c16. Parent now explicitly assigns minimal +Inspector begin/record interfaces and desktop-only helpers to this UI owner. +Write set: main Request/freeze/launch/rerun/run_material; carrier_execution.rs; +inspector.rs recorder methods only; run_sets.rs versioned native intent/binding; +scene_ui.rs retained force projection; scene_view.rs historical display context; +test fixture Request literal updates required to compile, no test execution unless +individually inspected and pure. Configured/session owner source/core untouched. + +Backend a4326a78a747bc868874fd16e434d780fd5a6d35 fetched exactly; diff from43ec505 +is only admission review/inventory. Core source remains byte-identical. No copied, +refreshed or claimed backend admission for this changed desktop source closure. + +Request adds per-track optional immutable ForceSnapshot values. Freeze before Worker/output admission; +None retains exact legacy methods/recording; Some (including empty) preserves scene +metadata and uses force-aware adapters/publication wrappers. Native saved comparisons +may hold per-member scenes: dispatch and recording use each member's snapshot, never +substitute the current draft or silently share a differing baseline. Material retains +snapshot in Request/history and core recorded material metadata. Draft/Retained scene +selection uses the retained document; forces selectable read-only in Retained, no +handles/edit; unknown legacy force metadata remains unknown, not inferred. + +Inspector existing legacy entry points delegate with None; new optional-snapshot +begin/record methods preserve identity, recorder, reader attach, selection and error +owners. Build RecordingRun.with_force_fields before StepRecorder::create; matching +constructor/accepted publication wrappers verify the same expected snapshot. Rebuild +actual run header with same force binding before existing config equality checks. +No projection/archive UI or core recording change; no new worker/session owner. + +FrozenSpec version1 remains byte/hash compatible: NativeCarrierSpec unchanged; +new optional ForceScene field omitted when None. Force-bearing FrozenSpec uses own +schema_version2 (global manifest/version1 remains unchanged), domain-separated +SHA256 over length-delimited legacy spec bytes + canonical binary force metadata. +Version1 requires None, version2 requires Some(valid scene), other/mixed versions +refuse. Bound original bytes before parsing, preserve duplicate-key rejection. +Native intent declaration version follows frozen spec, run header force digest and +selected publications must match. New comparison difference explicitly identifies +force metadata changes; reuse existing D1 paths. Rerun clones each stored scene and +preserves historical hashes, without automatic migration or current-draft fallback. + +Configured/provider/session/transport/free-surface authored requests refuse before +Worker/output creation. Existing immutable legacy reruns remain independent of draft. +Native carrier/emitter and particle-material dispatch use canonical backend methods; +no alternative evaluator, approximation, density conversion, gravity or dt policy. + +UI force launch stays visibly gated until separately authorized bounded integration +execution validates source custody. No numerical/SQLite/model tests in this turn. +Pure tests: literal FrozenSpec v1 golden encoding/hash, v2 canonical metadata roundtrip, +mutation/order/disabled/units/signedzero/version/unknown-field/oversize refusal; pure +Request metadata/support eligibility/freeze immutability + Scene retained projection. +Inspect entire helper paths, list-only and one full --exact name per execution. Any +solver/recorder/filesystem fixture is individually ignored/prepared only; no broad +filter/fallback. Compile/production Clippy/fmt + independent source review. Preserve +all earlier native/evidence/branches; rollback distinct incremental commit only. + +Reviewed refinements: Request stores a bounded per-track Vec> +(empty vector means all legacy; otherwise exactly methods.len). One fields_for_track +resolver drives steps, recorder setup/append and retained projection. Explicit None +in a saved member never falls back to another member/current draft. Freeze every +member before Worker/output, compare exact canonical bytes with stored intent. +Retained binding includes track identity and resets on each new Request; changing +track/document clears selection even when numeric IDs coincide. Resolve follow-live track before tree/properties and cache that track’s frame for +viewport composition. Timeline track intents project consistently on the next +paint; retained properties are read-only and labelled. + +All production launch/rerun routes remain gated before worker/filesystem admission. +A private cfg(test) Request admission flag may bypass only that temporary gate for +individually ignored, reviewed, separately authorized future integration probes. +No such workload is executed now. Version2 metadata differences use canonical bytes +including signedzero/None-vs-empty; run_sets/source.rs gains one exhaustive label. +Nested version failures use frozen-spec-specific errors, never trigger configured +manifest fallback. Inspector verifies expected force digest before create; None +rejects bound runs, Some matches exactly and must not double-bind an existing run. diff --git a/docs/plans/ui-refinement/reports/runtime-force-integration-plan-review.md b/docs/plans/ui-refinement/reports/runtime-force-integration-plan-review.md new file mode 100644 index 00000000..9d90ec7e --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-integration-plan-review.md @@ -0,0 +1,121 @@ +# Independent T5 runtime-force integration plan review + +## Disposition + +Conditionally suitable for bounded source implementation, with the custody and +compatibility conditions below. The newly assigned minimal Inspector recorder +interfaces close the previously identified ownership gap. No additional backend +physics or archive projection work is needed by this plan. Keep force-enabled UI +execution refused until separately authorized integration validation qualifies +the changed desktop closure. Source/pure-test success cannot supply that evidence. + +The owner accepted the review conditions before production edits. The reviewed +admission appendix now records the authoritative bounded per-track snapshot +vector/resolver, pre-worker freezing, track identity reset/rebinding, all-route +gates, exact force-byte comparisons, source-label update, version-error separation +and pre-create Inspector digest checks. No outstanding design blocker remains to +the bounded source slice; implementation/evidence gates below still apply. + +## Reviewed evidence + +- Worktree `/workspace/Rheon-ui-authoring`; HEAD + `8148fa239a60502a1245d5a69d0eeb8ceb905c16`. +- Admission `reports/runtime-force-integration-implementation.md`, SHA-256 + `f95b81065d133424c0bd68ab270bf5d4b95d37138eb6d27cec9d5e32ecc4a781`. +- Refinement re-read at the same HEAD: admission SHA-256 + `7baf8b570414f8c2405f9983d281fa776d30ad9e6a342540ea7450cda12d40b2`. +- Read Request validation/start/rerun/freeze/dispatch, CarrierExecution, Inspector + begin/record ownership, FrozenSpec/native manifests/binding/differences/read + limits, D1 source labels, SceneUi/scene projection, and canonical carrier, + particle and recording force adapter signatures/metadata checks. +- Backend `a4326a78a747bc868874fd16e434d780fd5a6d35` docs-only provenance is stated + by the admission. This review does not refresh backend admission or extend its + evidence to new desktop code. No build, test, native interaction, numerical, + SQLite, model or archive operation was performed. + +## Required conditions + +| Priority | Condition | Concrete acceptance | +| --- | --- | --- | +| High | One track-specific snapshot resolver must own precedence. | Saved native member intent supplies its own exact snapshot, including explicit absence; never use `.or(request.force_fields)` to substitute the top-level draft/baseline. Resolve/freeze every member before Worker/output admission, then use that immutable value for dispatch, constructor/accepted recording and retained presentation. Check two distinct scenes and a None member beside a Some member using pure metadata. | +| High | Retained selection must follow selected track/document identity. | Tree, properties, viewport and outline project the same retained document as the selected frame. Clear selection when track/document identity changes even if numeric object IDs coincide. Retained properties/handles remain read-only; legacy absence stays unknown, and Some(empty) stays explicitly authored empty metadata. | +| High | The unqualified-force gate must cover all user entry paths before side effects. | Fresh preview, saved-intent creation, loaded v2 rerun and any other start route refuse before Worker, directories, manifest or archive creation. Existing `freeze_native_set` and `rerun_saved` create output before `launch_request`, so a gate only there is too late. Legacy immutable None reruns stay independent of the current draft. | +| High | Expected snapshot/header association must be checked before creating the recorder. | Optional-snapshot begin methods compare the supplied run force digest with the expected Option snapshot before `StepRecorder::create`. None rejects a force-bound run; Some must match. Apply binding once: `bound_native_run` may already have bound the header. Accepted wrappers check the actual backend stamp rather than inserting expected metadata into a legacy accepted publication. | +| Medium | Force identity/differences must preserve canonical bytes. | v2 digest and ForceMetadata differences distinguish None/Some(empty), order, disabled objects, names, units and signed zero. Use canonical encoded bytes, not `ForceScene::PartialEq`, which equates positive/negative zero. Validate original bounded JSON with strict typed decoding, not a generic object roundtrip. | +| Medium | FrozenSpec versions must remain distinct from envelope-owner routing. | Dedicated FrozenSpec v1/v2 validation must not change manifest v1 or use nested-version errors to trigger configured-owner fallback. Keep v1 serialization/digest goldens and reject invalid version/payload combinations, unknown fields, duplicate keys, oversized input and unsupported versions. | + +Resolve the admission phrase “other/mixed versions refuse”: recommend refusing an +invalid schema/payload pair within one FrozenSpec while permitting independently +valid v1 None and v2 force-bearing members in manifest v1. Otherwise a D1 baseline +without authored metadata cannot naturally compare with a force-bearing member. +If mixed member versions are intentionally unsupported, document that limitation; +do not migrate historical v1 identity silently. + +The owner subsequently clarified that independently valid v1/v2 members are +allowed together in manifest v1 and only invalid per-FrozenSpec version/payload +pairs refuse. Carry this clarification into the contract/source; this resolves +the ambiguity without a historical migration. + +The admitted private `cfg(test)` Request flag may bypass only the temporary +unqualified-runtime gate for individually ignored future integration probes under +separate authorization. It must default false in all ordinary literals/harnesses, +have no production CLI/environment route, and never bypass snapshot freezing, +supported-owner eligibility, budget or run/publication binding validation. Its +presence does not authorize executing any numerical or SQLite probe now. + +## Source ownership and implementation shape + +The admitted minimal Inspector region is recorder begin/record interfaces and +their pure construction helpers only. Preserve existing reload/selected-step, +error, identity, limit, finish and reader owners. Do not copy separate Inspector +projection/archive work or edit core recording/solver/configured Session owners. +Add the small exhaustive difference-label match in `run_sets/source.rs` to the +exact write set: current native labels match every existing `SpecDifference`. +No comparison layout redesign is necessary. Update the canonical active milestone +and its superseded interface-preflight wording to match this actual authorization. + +Keep additive Inspector wrappers delegating legacy calls through None. Rebuild the +actual legacy header with its existing identity/provenance, apply the expected +optional force binding once, then retain the existing complete header equality +check. Both constructor and accepted publication paths use canonical wrappers. +Do not append bytes from the draft to accepted records. Core `require_fields` +already rejects absent/mismatched accepted custody; retain that failure behavior +and the accepted-but-unrecorded error distinction. Terminal recording, cancellation +and publication uncertainty stay owned by the current loop/recorder. + +Native carrier selects the existing source/emitter force adapter and maps its +existing step result; particle material selects its canonical force adapter with +the original emitter/config/cancel policy. None follows the exact old branch. +Some(empty) uses explicit force metadata even though its physical field is empty. +Do not alter dt shortening/retry, source schedule, gravity, density conversion, +force evaluation, projection or thread ownership. Configured/provider/Session, +transport and free-surface combinations explicitly refuse authored requests; +disabled/zero fields are still authored metadata, not a way around eligibility. + +## Compatibility and evidence gates + +Keep NativeCarrierSpec fields/order and v1 JSON spec digest unchanged. Omit the +new optional field when absent, preserving the complete v1 FrozenSpec and manifest +serialization. v2 uses a fixed distinct domain prefix and unambiguous fixed-endian +lengths around the old spec bytes and canonical backend force bytes. Document the +prefix/length format in code and pin an independent literal encoding/hash golden. +Opening/rerunning clones stored per-member metadata; it never re-freezes the +current draft into a historical spec. Bind declaration version, spec hash, run +force digest and every selected publication consistently during cold validation. +Keep existing pin/integrity/completeness distinctions and configured fallback. + +Before execution, inspect entire test/helper call paths. Pure literal tests may +cover v1 goldens, v2 codec/identity/mutation/version/size refusal, snapshot +immutability, support eligibility, track resolver precedence and read-only retained +projection. List-only enumeration precedes each fully qualified exact invocation. +Do not call existing numerical constructors, FrozenSpec capture_constructor, +Inspector begin/record, recording writers/readers or filesystem fixtures in these +pure tests. Build/format/production lint and independent source review are separate +implementation gates. Numerical/SQLite fixtures can be prepared and ignored for +later separately authorized bounded validation, with no execution in this turn. + +Prefer a distinct compatibility/wrapper checkpoint before dispatch/retained wiring +so failures and rollback remain attributable. Preserve all earlier source/native +evidence and unrelated branches. After source work, report what remains gated; +do not describe typed metadata tests as force runtime, archive parity, native +usability or scientific validation. diff --git a/docs/plans/ui-refinement/reports/runtime-force-integration-source-review.md b/docs/plans/ui-refinement/reports/runtime-force-integration-source-review.md new file mode 100644 index 00000000..989ae975 --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-integration-source-review.md @@ -0,0 +1,90 @@ +# Independent T5 runtime-force integration source review + +## Current disposition + +Suitable for a distinct gated source/pure-test checkpoint over `8148fa239a60502a1245d5a69d0eeb8ceb905c16` in `/workspace/Rheon-ui-authoring`. Production force execution remains refused. The additive custody/compatibility wiring is proportionate to the admitted desktop ownership. The two initial findings below are resolved in the final re-read source; no material source blocker remains. Source suitability and the focused passes do not qualify runtime/archive or native behavior. + +The reviewer read source and wrote only this report. No build, test, numerical/model/evaluator workload, SQLite/archive operation, native application, cleanup, commit or publication was performed. This review does not refresh backend admission or confer numerical/runtime/archive qualification on the new desktop closure. + +## Findings and required resolution + +1. **Retained composition ordering — repaired:** the initial implementation painted the tree before resolving follow-live and rebound only the viewport after timeline input. `prepare_scene_presentation` now resolves the frame/active track once before tree composition and binds its retained document. The cached frame remains the viewport input for that render frame. Timeline scalar intents commit for the next render frame, preserving one frame/document projection across tree, properties and viewport. On Draft→Retained, the bound-track helper deliberately avoids a second active-track resolution; the next preparation follows live normally. The synthetic wrapper mirrors this ordering. Pinned egui 0.33.3 input handling requests immediate repaint for pointer/key events (with an outstanding follow-up repaint), so normal interactive deferred intents have a subsequent paint; native integration is still unverified. +2. **Independent v2 evidence — repaired and focused probe passed:** the v2 probe now pins literal metadata hex and a literal v2 SHA from independently constructed Python little-endian/tag bytes, rather than only Rust roundtrip equality. It explicitly checks None-versus-Some(empty) ForceMetadata declarations and order reversal, alongside name/disabled/units/signed-zero changes. The source format agrees with the canonical codec's field/tag order and the distinct v2 domain/length framing. Its individually exact result passed; no numerical execution is requested or inferred. + +No other material source defect was found after re-reading the complete current integration diff, including production launch/output gates, recorder error/custody branches, v1 serialization and the narrow test fixture changes. The sealed source set and focused gate receipts below bind this disposition; future edits require a new review scope. + +## Request and execution custody + +Request owns a bounded `Vec>`: empty represents all-legacy, otherwise validation requires exactly one option per method. `fields_for_track` is the sole resolver used by carrier stepping, carrier constructor/accepted recording, material stepping/recording and retained projection. Explicit None is not substituted with another track or the mutable draft. Every present immutable snapshot passes canonical metadata encoding; saved members are independently frozen and compared as exact encoded bytes, including absence, before worker admission. + +All production literals use the private test-only admission flag false. Non-test compilation fixes admission false. A Some snapshot, including authored-empty metadata, is refused before Worker construction; configured/session/provider requests refuse before that flag can bypass the temporary gate. The flag does not bypass count, scene, saved binding, geometry, budget or supported-owner checks. No CLI/environment route exposes it. Fresh start also retains the existing draft-readiness refusal. `freeze_native_set` validates before output, and loaded reruns freeze stored per-member scenes and preflight before their first output path. Legacy reruns remain independent of the current draft. Test fixture changes only initialize the new fields as legacy/false; they do not authorize running those older fixtures. + +The native carrier Some branch calls the canonical `step_with_force_fields` or emitter variant with the existing requested dt, source/emitter and cancellation callback. None retains the original branch. Configured execution has a defensive explicit unsupported branch. Particle material uses the canonical optional field adapter with the original emitter/cancel policy and records the same immutable track-zero snapshot. The patch introduces no alternate evaluator, density conversion, gravity, dt, retry or scheduler policy. This is source inspection of dispatch, not evidence that a step ran or a field produced correct physics. + +## Recorder custody and compatibility + +Legacy Inspector begin/record methods delegate through None. Optional constructor paths bind the run once. The already-bound native begin method checks the expected force metadata digest before `StepRecorder::create`; None rejects a bound header and Some requires the same digest. Reconstructed actual headers apply the same binding before the existing full equality check. Accepted publication helpers require the backend's actual matching force stamp; they cannot synthesize missing accepted custody from the supplied draft. Existing recorder ownership, identity, limits, finish/error/reader handling remain in place. No independent core recording source is changed. + +FrozenSpec adds an omitted-when-None ForceScene field while NativeCarrierSpec is unchanged. Version 1 requires absence and retains the historical JSON spec hash/field order. Version 2 requires a valid present scene and hashes the fixed `rheon-desktop-native-force-spec-v2\0` domain followed by little-endian u64 byte lengths and the legacy JSON spec/canonical binary force bytes. Unknown or invalid version/payload pairs refuse. Explicit JSON null is rejected by the present-field deserializer; absence is the only legacy None spelling. Original input is bounded before strict typed decoding, preserving unknown/duplicate-key rejection. + +Manifest version remains 1, permitting individually valid v1/v2 members without migration. Native declaration version follows its FrozenSpec. Bound run creation validates the frozen spec and constructor; cold binding checks declaration version/hash and expected run force digest. Existing reader verification and decoded selected publications enforce run/publication metadata agreement. `ForceMetadata` differences compare canonical bytes, retaining signed-zero, order and None/empty distinctions rather than relying on float PartialEq. Existing comparison/source presentation receives only its new exhaustive label. Nested FrozenSpec version failures are separate from the envelope's configured-owner fallback sentinel. + +## Retained authoring boundary + +SceneUi stores a separate read-only retained document and `(track, optional digest)` identity. Begin/clear resets that binding; changes clear retained selection even if IDs coincide. Legacy absence stays unknown with explicit UI copy; Some(empty) is a known empty authored document. Properties are painted from a disabled copy, and selected-force/edit/handle paths remain Draft-only. Main passes editable only while Draft with no worker, and handle painting independently checks Draft. Scene projection now uses the context-specific document. Stored scenes do not mutate the editable draft. The repaired composition uses a cached per-render frame/document; timeline selection changes appear together on the next frame. Nonempty retained authored documents also retain the lab-mode guard, so unrelated lab owners cannot silently substitute an unforced workflow. + +## Pure-probe admission + +The complete new `force_runtime_ui_tests.rs` file and helper paths were inspected before admission, then re-read with the added goldens and composition test. Seven individually enumerated exact names are source-safe: + +- `force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact` +- `force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact` +- `force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize` +- `force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members` +- `force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io` +- `force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection` +- `force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents` + +Those helpers use literal serde metadata, NativeCarrierSpec/FrozenSpec validation, canonical codec/hash, scalar Request preflight, literal RecordingRun binding and SceneUi/default Context paint. Geometry/BufferPlan construction performs arithmetic only; ForceScene freeze validates and clones without sampling acceleration. The seventh helper adds bounded in-memory LiveHistory reservation and direct publication of two eight-byte literal PreviewFrames, plus App default/prepare calls. It never starts or polls the App. `prepare_scene_presentation` reaches the default readerless Inspector; selection fails before the reader closure and I/O. Worker stays None, so App drop has nothing to cancel/join. The tests never call `create_simulation`, capture, Worker/App update, Inspector begin/record/reload/readers, recording writers, filesystem fixtures or cleanup. Inspector binding helpers expose only the pure association operations to sibling tests. Generic JSON values are used to manufacture malformed test inputs; production decoding still receives original bytes. The seventh probe manually applies scalar track intent and checks projections; it is not native pointer/dropdown or complete App-update evidence. + +No numerical, SQLite or native probe is admitted here. Prepared ignored integration tests, if later added, require a separate complete helper and authorization review. Existing backend and earlier UI/native evidence do not qualify this changed execution/recording closure. Final source/pure-test/build receipts must preserve that boundary, and the visible launch gate must remain until separately authorized bounded runtime/archive validation. + +## Focused evidence and regression re-admission + +Read-only receipt of `/tmp/rheon-ui-t5-list4.log`: 197 tests, zero benchmarks. `/tmp/rheon-ui-t5-exact1.log` through `exact7.log` each name one corresponding admitted test above and record one pass, zero failures, 196 filtered out. These are metadata/Context assertions, including the independent literal v2 golden; they provide no numerical or SQLite result. A final transfer manifest has not yet been received. + +The following six existing regression names were independently re-inspected with their complete current helper paths and admitted only for individual exact execution after enumeration: + +- `native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless` +- `scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context` +- `scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history` +- `scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview` +- `scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off` +- `timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states` + +The synthetic presentation helper retains the previously inspected workerless/readerless construction and disabled Appearance guard; it now mirrors cached preparation rather than refetching the active track mid-frame. The SceneUi pointer helper composes only SceneUi and literal input; its retained roundtrip helper uses App default and a direct PreviewFrame with no history or worker. The scene-pick helper builds projection from literal vertices/triangles/force objects and a camera, never CollisionMesh/model constructors. The handles helper directly invokes handle update/paint with literal SceneUi/camera/Context, never Viewport or App update; its cancellation assertion does not establish full camera fallthrough behavior. The timeline helper paints direct PreviewFrame/report structs and a literal Request, with no history; supplied report values are not calculated results. None of these helper paths starts a Worker, constructs/steps a solver, opens SQLite, attaches an Inspector reader, creates a recorder, or performs filesystem cleanup. App-containing helpers keep worker None through drop. + +Read-only receipt of `/tmp/rheon-ui-t5-regression1.log` through `regression6.log`: all six requested names passed individually, one pass and 196 filtered out per log. Together with the seven new names this is thirteen distinct exact pure/Context passes, not a broad suite. Locked desktop check3 passed in 1.43 s and strict production Clippy1 in 4.02 s. The parent confirms no production edits after these checks; hash reads agree with the final inspected source. Format/diff and a sealed transfer packet, if later supplied, remain separate receipts. No native entry or numerical/SQLite integration probe ran as part of these thirteen logs. + +## Final reviewed source seals + +All paths below are relative to the repository. The unrelated source/numerical owners are outside this write set; plan/docs are not included as production source seals. + +| File | SHA-256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `0cc6bd651a60b2d09abc77bd49515a114d993faa842acebf19e3955ad9b7a62e` | +| `src/bin/desktop/carrier_execution.rs` | `c4455879373fc71eaaf398fdaa230c4d0b115dc7ff0a9da6356afeb24cba23b2` | +| `src/bin/desktop/inspector.rs` | `aad6760e94582d6512c32387609c3c2874e92728c93c3c7d2089cbf3bc6ee62c` | +| `src/bin/desktop/run_sets.rs` | `b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0` | +| `src/bin/desktop/run_sets/source.rs` | `39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf` | +| `src/bin/desktop/comparison.rs` | `27ad4b88db848430a68f5f9fae796fa10eab1df26af9dbae73386e55c9b5c74f` | +| `src/bin/desktop/scene_ui.rs` | `cc866e716876dfee8c9245c2473426c7c3253b2b0787b49b907202a6e1402557` | +| `src/bin/desktop/scene_view.rs` | `d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f` | +| `src/bin/desktop/scene_handles.rs` | `4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22` | +| `src/bin/desktop/force_runtime_ui_tests.rs` | `0f8b88db0553cf079c0f78a2e41766fcb6160f6dda1a3d117f1ce93fe17b91f5` | +| `src/bin/desktop/native_presentation_harness.rs` | `38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6` | +| `src/bin/desktop/configured_worker_tests.rs` | `dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016` | +| `src/bin/desktop/setup_ui_tests.rs` | `db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900` | +| `src/bin/desktop/timeline_ui_tests.rs` | `915c565a2be34618a4be8ad801e215a2abe15a7eaf801fb2e5353bf632bdad54` | + +The last three fixture files only initialize legacy empty/false Request fields, as do unrelated existing Request literals in main. They were not executed merely because they compile. Changed synthetic harness source does not retroactively change the older sealed native binary or its evidence; a later native build needs a fresh source/binary seal. diff --git a/docs/plans/ui-refinement/reports/runtime-force-validation-contract-review.md b/docs/plans/ui-refinement/reports/runtime-force-validation-contract-review.md new file mode 100644 index 00000000..02daa109 --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-validation-contract-review.md @@ -0,0 +1,142 @@ +# Independent T5 bounded validation contract review + +## Disposition + +The proposed two-case design can produce honest, narrow desktop dispatch/custody +evidence. It is not execution-ready. The contract correctly states that both +ignored numerical/SQLite test bodies are absent, have not been enumerated, and +are absent from the current reported 197-name binary. The existing pure passes +do not close runtime/archive acceptance or admit a force-enabled production launch. + +Final re-read confirms the owner corrected the material accepted-time wording +and stale canonical source-state entries. No further docs-only design blocker +remains. The absent bodies, sealed executable, existing enforcement and separate +execution admission remain concrete prerequisites, not optional suggestions. + +No numerical, SQLite, build, test, native or supervisor action was performed by +this review. Only `runtime-force-validation-contract.md`, the current canonical +plan and `runtime-force-integration-implementation.md` were read. Fixture/API +claims below are contractual requirements, not a new independent source audit or +confirmation that missing test bodies compile. + +Final reviewed document SHA-256 identities: + +- Validation contract: `7327a76ef2d66525badf61d73518ad56009c2e54149ad181f703516890c3f2c5`. +- Canonical plan: `e9bccc7035a4821f1c14c3be864d4b74190df12fd4fde10e3c697cca3ff6763c`. +- Implementation admission: `8869e2509fdf21fdf2318beeb3ed9be2389d026670756cbf746d2a0201c21f9c`. + +## Execution blockers and prerequisite evidence + +1. **Exact test bodies are absent.** Implement the two proposed cases as ignored + tests in the separately owned desktop test module. Independently inspect every + constructor, dispatch, recorder/read, filesystem and assertion helper before + compiling/listing; there must be no broad fixture or fallback execution. The + exact compiled list must show both names with ignored status. Do not transfer + the older 197-name binary as the integration executable. +2. **The final source/executable closure is not yet sealed.** Pin the final + commit/tree and affected-source manifest, test/helper bytes, Cargo.lock, + toolchain/features and exact executable hash after those bodies exist. Verify + that the executor's source/binary matches those seals. A backend admission for + unchanged core does not admit the changed desktop/test closure. +3. **Existing containment has not been demonstrated by this review.** The + execution owner must identify its already supported wall/CPU/address-space, + per-file/aggregate-filesystem and captured-output enforcement, and process-group + termination/survivor checks. Per-file limits or post-run file counting alone + do not enforce the separate aggregate-root bound. If any required existing + control is missing, report that exact prerequisite and stop that execution. + No cgroup/mount provisioning, delegation/permission changes or substitute + unbounded run is admitted. +4. **Execution remains separately unadmitted.** This contract and this review do + not authorize numerical/SQLite execution. This is a present task boundary, + not a request for an optional approval flow. Continue only independently + authorized preparation; production launch stays gated. + +## Fixture and interface conditions + +Case 1 is small and discriminating for custody: one native Jacobi track, one +requested interval, eight cells, one nonzero canonical directional field, +Some snapshot, saved native FrozenSpec v2 inside manifest v1, and two expected +constructor/accepted publications. The body must make the frozen density, +tolerances, source absence and step policy authoritative through the saved member, +rather than silently using normal preview defaults. Request, saved member and +recording must resolve to the same exact canonical bytes. Fixed-dt refusal remains +an expected failure boundary; it is not permission to tune dt or rerun on failure. + +Case 2 is a separate material custody path, not a saved-material-rerun format. +Its stated Water/Air, fill, sample count, max-particle count, gravity, temperature +and memory values must be explicit in the inspected body. In particular, verify +that the selected constructor/fill with no emitter actually yields the stipulated +64 initial particles; do not infer that from `samples_per_axis` or cap alone, or +change the fixture after a failed run. If source preflight contradicts this +fixture, amend/review the contract before execution. One public material step may +perform up to 16 internal preparations under the existing backend retry policy; +do not describe it as one low-level evaluation or add an outer retry. + +**Reviewed correction, resolved:** Case 2 now requires previews using actual +accepted state/time and preview/record time/dt association. Accepted dt can differ +from requested dt under the existing preparation policy. The future body must +assert generation, preview time, recorded time/dt and accepted controls consistently +against actual accepted state, without fabricating a literal accepted time or +requiring requested dt without a corresponding policy. Constructor time is zero. +No physics change was needed to correct the contract. + +The test flag may bypass only the temporary force-validation gate. It must leave +snapshot freeze, supported-owner, immutable per-track intent, budget and run/step +binding validation intact. Do not call `Worker::start`, App update/start/rerun, +GUI, provider/model/child, emitter, collision mesh or observer paths. Keep exactly +one track and one requested interval per case. The owned scheduler/cancel/progress/ +history arguments needed by `run_owned` must be explicit in the inspected helper +closure; no hidden test helper may launch another execution owner. + +Desktop enables Rayon. Request threads=1 inside the production scoped pool means +**one owned Rayon pool worker**; the process also has its test/supervisor context. +This is not a no-parallel build, zero-worker execution or proof of a total OS +thread count. Preserve that wording in packet/results. Libtest threads=1 controls +test concurrency; selecting one fully qualified ignored exact name controls which +workload is requested. Neither substitutes for process/resource containment. + +## Filesystem, teardown and outcome handling + +Each case receives a different explicit absolute, fresh, exclusively owned root +created once by the existing supervisor. Helpers have no temp-directory/default +recording-directory fallback, do not replace the root, and create no paths outside +it. Verify path containment under the supervisor's protected-root policy. No Drop, +panic/success/failure cleanup, deletion or retry belongs in the body. Retain the +database, sidecars, manifests and failure evidence on every outcome. + +The stated limits are requests for enforcement, not observed measurements: 30 s +wall, 20 s CPU, 1 GiB address space, 1 MiB per file, 8 MiB aggregate root, 1 MiB +captured output, then TERM/owned process-group KILL after 1 s. State whether a +bound or timeout fired; do not call a terminated run a failed scientific result +or infer peak resources from these limits. Stop after unexpected output, +selection, resource failure or timeout; preserve its disposition before any +further action. After termination, check owned descendants and inventory/hash +retained files before claiming immutable final evidence. Binary/source **seals** +can accompany the small root; do not imply that a large compiled executable must +fit inside the 8 MiB data budget. + +## What passing cases can and cannot establish + +A valid Case 1 result can establish one saved-native constructor-to-step dispatch, +matching force-bound header/publications, finalized member pin/hash/status and +cold-open association for this tiny request. Case 2 can establish corresponding +particle Request/record custody. Check exact metadata bytes after separate draft +mutation, progress/result count, publication count/order/generation, actual accepted +controls and recording status. Preserve integrity separately from acknowledged +recording completeness and any accepted-but-unrecorded tail on failure. + +These cases do not validate force physics, legacy numerical equivalence, density +conversion, all pressure solvers, mixed saved members, frozen rerun, cancellation, +Worker admission/join, UI gestures or native presentation. Those remain distinct +bounded gates. Passing both is a review checkpoint, not automatic removal of the +temporary production gate. The workerless synthetic harness supplies none of +these runtime/archive claims. + +## Canonical plan consistency + +The final re-read confirms S3/S4/S6, Blockers and the former interface-preflight +section now distinguish implemented source from pending runtime/native evidence. +Minimal Inspector recorder ownership is explicit, thirteen exact inert passes +are scoped, and the proposed tests remain explicitly absent. This resolves the +earlier stale-state finding. Historical native evidence remains tied to its own +revision; no acceptance status advanced merely to repair wording. diff --git a/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md new file mode 100644 index 00000000..6e32d5d6 --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md @@ -0,0 +1,123 @@ +# T5 bounded desktop validation contract — proposed, not executed + +Status: source integration is gated. This document specifies the next exact cases; +those two numerical/SQLite test bodies are **not yet implemented or enumerated**. +It is not an executable admission, workload authorization or runtime result. The +current executable probes are the seven pure tests in force_runtime_ui_tests.rs. +No force-enabled production launch is admitted by source-only evidence. + +## Ownership and prerequisites + +Use canonical backend core at43ec505218c43594641ecbb80873bec8c88d7d2c; fetched +backend a4326a78a747bc868874fd16e434d780fd5a6d35 differs only in its own admission +review/inventory. Preserve legacy hashes. UI owns Request/carrier dispatch, +Inspector begin/record, frozen desktop intent/binding and retained presentation; +backend remains owner of force evaluation and recording/accepted solver custody. + +Before execution, implement the two separately ignored cases in a separate desktop +test module, independently inspect their complete helpers and enumerate exact names. +Seal the final commit/tree, complete affected-source SHA256 manifest, Cargo.lock, +Rust version/feature configuration, test-source bytes and exact compiled binary. +The current197-name binary does not contain these cases and must not be represented +as execution-ready integration evidence. Compile/list-only does not step a solver. +No tuning, retry or fallback suite is implied by this contract. + +The execution owner must revalidate its existing supervisor and fresh bounded +recording filesystem. Do not provision mounts/cgroups, change delegation or reuse +a previous output root. If an existing suitable bound is absent, return the exact +missing prerequisite. Existing live cancellation stays parked; this contract does +not replace its containment requirements or supervise a native UI session. + +## Common bounded execution envelope + +One exact ignored case per disposable process, serially, libtest threads1. Desktop +features include rayon: use the production run_owned scope with Request.threads=1; +this makes one owned Rayon pool worker, rather than claiming parallel is disabled. +No Worker::start, GUI/event loop, model/provider, child command, emitter, collision +mesh, observer, progress/failure gate or public network input is present. + +Require the execution owner's already supported controls before dispatch: + +- wall30s, CPU20s, address space1GiB; TERM then owned process-group KILL after1s; +- per-file1MiB and aggregate fresh root8MiB, aggregate captured output1MiB; +- root created once without replacement by the supervisor, exclusive to this case; +- no automatic success/failure/panic/timeout teardown or cleanup; retain database, + sidecars, manifests, stdout/stderr, source/binary seals and disposition; +- enumerate all retained files/bytes and SHA256 after the child terminates; check + no owned descendants remain. Do not infer measured peak resources from limits. + +The test helper must accept an explicit absolute supervisor-owned root with no +fallback to temp_dir or normal recording_directory. It must not delete existing +paths or use a Drop cleanup guard. Every path is inside that fresh root. Stop on +any unexpected selection, file, resource-bound failure or timeout; preserve evidence. +These are maximum requests, not promised observed consumption. + +## Case1 — proposed exact name + +`force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2` + +A single JacobiPcgV1 native carrier track, grid2×2×2, spacing[0.5;3]m, +origin[0;3], density2kg/m³, zero initial velocity/tracer, one requested interval +0.000001s. Frozen tolerances: relative1e-11, absolute1e-12, predicted divergence +1e-9/s, actual divergence1e-6/s, Courant1, max pressure iterations100; retained-array +limit1MiB. No source/emitter. Request preview edge2, history budget16MiB, +recording max database1MiB/record65536bytes/records2, steps1/threads1. + +Canonical scene version1 has one enabled force: id1, name“Desktop custody probe”, +translation[0.5;3]m, identity xyzw quaternion, Acceleration, Directional vector +[0.25,0,0]m/s², Global support, activation[0,∞). No configured gravity is inserted +by this test; native carrier has its existing zero-gravity inputs. + +Freeze once. Build native FrozenSpec v2 and one-member RunSetRevision v1 with no +prior attempt; bind the same Some snapshot in Request. Permit only the private +cfg(test) force_validation flag, preserving every other preflight. Through existing +run_owned → run_simulations → CarrierExecution::advance and Inspector optional +constructor/record wrappers, request exactly ONE step (max100 pressure iterations). +No retry loop around the public request. Native saved-intent fixed-dt policy still +rejects a shortened dt and retains the exact accepted archive. + +Expected evidence: one result row and progress1; two ordered in-memory frames at +constructor/accepted times; source binding, actual accepted pressure controls and +same force metadata on constructor and accepted records. Open final immutable member +through RunSetRevision/open_member, verify pin/hash/status Completed, count2, +generation1, exact stored force bytes and spec/declaration/run binding. Mutate a +separate draft after freeze; it must change neither archive nor retained snapshot. +Existing startup/finished manifests and database remain. This proves dispatch and +custody for this tiny case, not field physics, legacy equivalence or UI interaction. + +## Case2 — proposed exact name + +`force_runtime_integration_tests::material_force_dispatch_records_immutable_scene` + +One material track with same2×2×2 preview/grid geometry in a1m cube. Water at +293.15K; fill EmittedFluid, samples_per_axis4, max_particles64, initially exactly64 +particles, zero velocity, no emitter. Default water material and Air ambient, +ambient293.15K, gravity[0,-1,0]m/s², liquid sound speed20, wall heat exchange1, +retained-array limit1MiB. Freeze the same enabled canonical force document as Case1. +One requested interval1e-6s, at most16 internal preparation/retry attempts; no +external retry, pressure solver or max-pressure-iteration loop in this path. +Other Request/recording/preview limits match Case1. No saved native set is claimed +for material; immutable Request and version1 core force metadata own this path. + +Through existing run_owned → run_material, exactly ONE public particle step, same +snapshot for begin/step/record. Expected progress1, one result row, constructor and +accepted previews using the actual accepted state/time, cold core reader verification and +RecordedMaterial force document matching exact run-bound bytes, accepted dt/emitter/ +report controls and generation1. Assert preview/record time and actual dt association; +particle preparation may shorten dt, so do not assert a fabricated literal time or +necessarily the requested1e-6s. Both records must remain associated with that +snapshot after separate draft mutation. Preserve database/sidecars and metadata; +no automatic cleanup. Do not describe this as a material frozen-rerun format that +does not exist in the current application. + +## Subsequent gates, deliberately separate + +After the reviewed exact bodies pass once under separately admitted execution, +review their evidence before removing the temporary production gate. Native active +force launch, progress/Cancel/join, retained-versus-draft gestures, long status, +keyboard/accessibility, mixed saved members and frozen rerun still need their own +bounded evidence. Do not silently add those workloads to the above two-case budget. +The current no-force workerless synthetic harness cannot establish any of them. +Existing actual8148fa2 native validation remains owned by the current validator; +it does not cover this T5 source. JPEGs must come from actual rendered exact-source +states, labelled with revision and state, quality85, confirmed Library identity. diff --git a/src/bin/desktop/carrier_execution.rs b/src/bin/desktop/carrier_execution.rs index 48191c0b..0f7f8ab9 100644 --- a/src/bin/desktop/carrier_execution.rs +++ b/src/bin/desktop/carrier_execution.rs @@ -13,12 +13,16 @@ const GUIDANCE_PIXEL_BUDGET: usize = 1024 * 1024; pub(crate) enum AdvanceError { Native(rheon::SimulationError), Session(rheon::SessionError), + Unsupported, } impl std::fmt::Display for AdvanceError { fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Native(error) => error.fmt(out), Self::Session(error) => error.fmt(out), + Self::Unsupported => out.write_str( + "Authored fields require native carrier execution; configured owner unsupported", + ), } } } @@ -51,6 +55,7 @@ impl AdvanceError { ) | LiquidStepError::Volume(LiquidVolumeError::Cancelled { .. }), )) => Cause::Cancelled, + Self::Unsupported => Cause::StartupFailure, _ => Cause::NumericalFailure, } } @@ -128,9 +133,26 @@ impl CarrierExecution { request: &Request, source: Option, cancelled: &AtomicBool, + fields: Option<&rheon::ForceSnapshot>, ) -> Result { match self { Self::Native(sim) => { + if let Some(fields) = fields { + return if let Some(emitter) = request.emitter { + sim.step_with_emitter_and_force_fields( + request.requested_dt, + emitter, + fields, + |_| cancelled.load(Ordering::Relaxed), + ) + } else { + sim.step_with_force_fields(request.requested_dt, source, fields, |_| { + cancelled.load(Ordering::Relaxed) + }) + } + .map(|report| report.step) + .map_err(AdvanceError::Native); + } let result = if let Some(emitter) = request.emitter { sim.step_with_emitter(request.requested_dt, emitter, |_| { cancelled.load(Ordering::Relaxed) @@ -142,6 +164,7 @@ impl CarrierExecution { }; result.map_err(AdvanceError::Native) } + Self::Configured(_) if fields.is_some() => Err(AdvanceError::Unsupported), Self::Configured(track) => track .advance(None) .map(|report| report.carrier.step) diff --git a/src/bin/desktop/comparison.rs b/src/bin/desktop/comparison.rs index 267244cd..a752ed0b 100644 --- a/src/bin/desktop/comparison.rs +++ b/src/bin/desktop/comparison.rs @@ -998,6 +998,7 @@ fn difference_label(d: SpecDifference) -> &'static str { SpecDifference::Source => "source schedule", SpecDifference::MemoryBudget => "memory budget", SpecDifference::RecordingBudget => "recording budget", + SpecDifference::ForceMetadata => "force metadata", } } fn unavailable_label(r: Unavailable) -> &'static str { diff --git a/src/bin/desktop/configured_worker_tests.rs b/src/bin/desktop/configured_worker_tests.rs index 55f2c824..04521869 100644 --- a/src/bin/desktop/configured_worker_tests.rs +++ b/src/bin/desktop/configured_worker_tests.rs @@ -485,6 +485,9 @@ fn actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel() { let (reached, at_failure) = std::sync::mpsc::sync_channel(0); let (release, resume) = std::sync::mpsc::sync_channel(0); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 2, steps: 3, diff --git a/src/bin/desktop/force_runtime_ui_tests.rs b/src/bin/desktop/force_runtime_ui_tests.rs new file mode 100644 index 00000000..76001555 --- /dev/null +++ b/src/bin/desktop/force_runtime_ui_tests.rs @@ -0,0 +1,407 @@ +//! In-memory custody/intent probes only; never construct or step a solver. +use super::*; +use rheon::{ + ForceKind, ForceObject, ForceScene, ForceSnapshot, ForceSupport, ForceTime, ForceTransform, + ForceUnits, SceneObjectId, +}; +use run_sets::{FrozenSpec, NativeCarrierSpec, RunSetMember, RunSetRevision, SpecDifference}; +const V1_SPEC: &str = r#"{"counts_xyz":[2,2,2],"spacing_m":[0.5,0.5,0.5],"origin_m":[0.0,0.0,0.0],"density_kg_m3":2.0,"pressure_method":"jacobi_pcg_v1","tolerances":{"relative_residual":1.0,"absolute_residual_m3_s2":1.0,"predicted_divergence_per_s":1.0,"actual_divergence_per_s":1.0,"max_courant":1.0,"max_pressure_iterations":4},"requested_dt_s":0.125,"requested_steps":1,"source":null,"simulation_memory_limit_bytes":1048576,"recording":{"max_database_bytes":65536,"max_record_bytes":16384,"max_records":2}}"#; +fn spec() -> FrozenSpec { + FrozenSpec::new(serde_json::from_str::(V1_SPEC).unwrap()).unwrap() +} +fn scene() -> ForceScene { + ForceScene { + version: 1, + objects: vec![ForceObject { + id: SceneObjectId(1), + name: "Recorded force".into(), + enabled: false, + transform: ForceTransform { + translation: [-0.0, 0.5, 0.5], + ..Default::default() + }, + units: ForceUnits::ForceDensity, + kind: ForceKind::Vortex { strength: -0.25 }, + support: ForceSupport::Box { + half_extent: [0.1, 0.2, 0.3], + edge_fraction: 0.25, + }, + time: ForceTime { + start_seconds: 0.125, + end_seconds: Some(0.5), + }, + }], + } +} +fn metadata(fields: &ForceSnapshot) -> Vec { + rheon::recording::encode_force_metadata(fields).unwrap() +} +fn set(specs: Vec, difference: Vec) -> RunSetRevision { + RunSetRevision { + schema_version: 1, + set_id: "literal-set".into(), + parent_sha256: None, + baseline_member: "member-0".into(), + members: specs + .into_iter() + .enumerate() + .map(|(i, spec)| RunSetMember { + member_id: format!("member-{i}"), + spec, + differences_from_baseline: if i == 0 { vec![] } else { difference.clone() }, + attempt: None, + }) + .collect(), + } +} +#[test] +fn force_intent_legacy_v1_bytes_and_digest_remain_exact() { + use sha2::{Digest, Sha256}; + let original = spec(); + assert_eq!(serde_json::to_string(&original.spec).unwrap(), V1_SPEC); + assert_eq!( + format!("{:x}", Sha256::digest(V1_SPEC.as_bytes())), + "5969e6f1a25728943a3a1119d4f393f33a4bb45af71a5d99a54551d06d569519" + ); + assert_eq!( + original.sha256, + <[u8; 32]>::from(Sha256::digest(V1_SPEC.as_bytes())) + ); + let legacy = format!( + "{{\"schema_version\":1,\"sha256\":{},\"spec\":{V1_SPEC}}}", + serde_json::to_string(&original.sha256).unwrap() + ); + assert_eq!(serde_json::to_string(&original).unwrap(), legacy); + let decoded = FrozenSpec::from_bytes(legacy.as_bytes()).unwrap(); + assert_eq!(decoded, original); + assert!(decoded.force_snapshot().unwrap().is_none()); + assert_eq!(serde_json::to_vec(&decoded).unwrap(), legacy.as_bytes()); +} +#[test] +fn force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact() { + let mut draft = scene(); + let fields = draft.freeze().unwrap(); + let original = spec(); + let forced = original.clone().with_force_fields(&fields).unwrap(); + assert_eq!(forced.schema_version, 2); + // Independently constructed with Python struct.pack little-endian scalars, + // literal tags/bytes and hashlib; not the Rust encoder/decoder roundtrip. + let literal_hex = "5248454f4e46460001000000010000000100000001000000000000000e0000005265636f7264656420666f726365000000000000000080000000000000e03f000000000000e03f000000000000000000000000000000000000000000000000000000000000f03f0102000000000000d0bf029a9999999999b93f9a9999999999c93f333333333333d33f000000000000d03f000000000000c03f01000000000000e03f"; + let encoded_hex: String = metadata(&fields) + .iter() + .map(|b| format!("{b:02x}")) + .collect(); + assert_eq!(encoded_hex, literal_hex); + assert_eq!( + forced + .sha256 + .iter() + .map(|b| format!("{b:02x}")) + .collect::(), + "fd129900f78968456c52e42562d7a860fae3885f180f6d72717da127a0bfcbcb" + ); + assert_ne!(forced.sha256, original.sha256); + let bytes = serde_json::to_vec(&forced).unwrap(); + let restored = FrozenSpec::from_bytes(&bytes).unwrap(); + assert_eq!( + metadata(&restored.force_snapshot().unwrap().unwrap()), + metadata(&fields) + ); + draft.objects[0].enabled = true; + draft.objects[0].name = "Changed draft".into(); + assert_eq!( + restored.force_fields.unwrap().objects[0].name, + "Recorded force" + ); + let empty = ForceScene::default().freeze().unwrap(); + let empty_spec = spec().with_force_fields(&empty).unwrap(); + assert_ne!(empty_spec.sha256, original.sha256); + assert!( + set( + vec![original.clone(), empty_spec.clone()], + vec![SpecDifference::ForceMetadata] + ) + .validate() + .is_ok() + ); + assert!( + set(vec![original.clone(), empty_spec], vec![]) + .validate() + .is_err() + ); + let mut ordered = fields.scene().clone(); + let mut second = ordered.objects[0].clone(); + second.id = SceneObjectId(2); + ordered.objects.push(second); + let forward = spec() + .with_force_fields(&ordered.freeze().unwrap()) + .unwrap(); + ordered.objects.reverse(); + let reverse = spec() + .with_force_fields(&ordered.freeze().unwrap()) + .unwrap(); + assert_ne!(forward.sha256, reverse.sha256); + assert!( + set(vec![forward, reverse], vec![SpecDifference::ForceMetadata]) + .validate() + .is_ok() + ); + assert!( + set( + vec![original.clone(), forced.clone()], + vec![SpecDifference::ForceMetadata] + ) + .validate() + .is_ok() + ); + assert!( + set(vec![original, forced.clone()], vec![]) + .validate() + .is_err() + ); + for mutation in 0..4 { + let mut changed = fields.scene().clone(); + match mutation { + 0 => changed.objects[0].transform.translation[0] = 0.0, + 1 => changed.objects[0].enabled = true, + 2 => changed.objects[0].units = ForceUnits::Acceleration, + _ => changed.objects[0].name = "Renamed".into(), + }; + let changed = spec() + .with_force_fields(&changed.freeze().unwrap()) + .unwrap(); + assert_ne!(changed.sha256, forced.sha256); + assert!( + set( + vec![forced.clone(), changed], + vec![SpecDifference::ForceMetadata] + ) + .validate() + .is_ok() + ); + } +} +#[test] +fn force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize() { + let forced = spec() + .with_force_fields(&scene().freeze().unwrap()) + .unwrap(); + let value = serde_json::to_value(&forced).unwrap(); + for (key, v) in [ + ("schema_version", serde_json::json!(1)), + ("schema_version", serde_json::json!(3)), + ("force_fields", serde_json::Value::Null), + ] { + let mut bad = value.clone(); + bad[key] = v; + assert!(FrozenSpec::from_bytes(&serde_json::to_vec(&bad).unwrap()).is_err()); + } + let mut bad = value.clone(); + bad.as_object_mut().unwrap().remove("force_fields"); + assert!(FrozenSpec::from_bytes(&serde_json::to_vec(&bad).unwrap()).is_err()); + let mut bad = value.clone(); + bad["force_fields"]["objects"][0]["name"] = serde_json::json!("Tampered"); + assert!(FrozenSpec::from_bytes(&serde_json::to_vec(&bad).unwrap()).is_err()); + let mut bad = value.clone(); + bad["unknown"] = serde_json::json!(true); + assert!(FrozenSpec::from_bytes(&serde_json::to_vec(&bad).unwrap()).is_err()); + let json = serde_json::to_string(&forced).unwrap(); + let duplicate = json.replacen("{", "{\"schema_version\":2,", 1); + assert!(FrozenSpec::from_bytes(duplicate.as_bytes()).is_err()); + let mut bad = value; + bad["schema_version"] = serde_json::json!(99); + assert!(matches!( + FrozenSpec::from_bytes(&serde_json::to_vec(&bad).unwrap()), + Err(run_sets::RunSetError::Unsupported( + "frozen native intent version" + )) + )); + assert!(FrozenSpec::from_bytes(&vec![b' '; 1024 * 1024 + 1]).is_err()); +} +fn request() -> Request { + Request { + force_fields: Vec::new(), + force_validation: false, + collision_mesh: None, + size: 2, + steps: 1, + tight: false, + threads: 1, + methods: vec![PressureImplementation::JacobiPcgV1], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Edge(2), + domain_side: 1.0, + requested_dt: 0.125, + material: None, + material_color: [0; 3], + recording_limits: rheon::recording::RecordingLimits { + max_database_bytes: 65536, + max_record_bytes: 16384, + max_records: 2, + }, + recording_directory: "unused-no-output".into(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + } +} +#[test] +fn force_request_track_resolver_gates_before_work_and_binds_saved_members() { + let fields = scene().freeze().unwrap(); + let mut r = request(); + r.force_fields = vec![Some(fields.clone())]; + assert!( + validate_request(&r) + .unwrap_err() + .contains("validation is pending") + ); + r.force_validation = true; + assert!(validate_request(&r).is_ok()); + let forced = spec().with_force_fields(&fields).unwrap(); + r.methods = PressureImplementation::ALL.to_vec(); + r.force_fields = vec![None, Some(fields.clone())]; + r.saved_set = Some(set( + vec![spec(), forced], + vec![SpecDifference::ForceMetadata], + )); + assert!(validate_request(&r).is_ok()); + assert!(r.fields_for_track(0).is_none()); + assert_eq!(metadata(r.fields_for_track(1).unwrap()), metadata(&fields)); + r.force_fields = vec![Some(fields.clone()), None]; + assert!( + validate_request(&r) + .unwrap_err() + .contains("snapshot mismatch") + ); + r.saved_set = None; + r.force_fields = vec![Some(fields)]; + assert!(validate_request(&r).unwrap_err().contains("track count")); + r.force_fields.clear(); + r.force_validation = false; + assert!(validate_request(&r).is_ok()); +} +#[test] +fn force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io() { + let legacy = rheon::recording::RecordingRun { + identity: rheon::SessionIdentity { + run_id: 7, + source_revision: "literal".into(), + }, + solver: "literal-solver".into(), + configuration: "literal-config".into(), + counts: [1; 3], + spacing: [1.0; 3], + origin: [0.0; 3], + provenance: "literal-provenance".into(), + }; + let fields = scene().freeze().unwrap(); + let bound = inspector::bind_force_fields(legacy.clone(), Some(&fields)).unwrap(); + assert!(inspector::verify_force_fields(&legacy, None).is_ok()); + assert!(inspector::verify_force_fields(&legacy, Some(&fields)).is_err()); + assert!(inspector::verify_force_fields(&bound, None).is_err()); + assert!(inspector::verify_force_fields(&bound, Some(&fields)).is_ok()); + assert!(inspector::bind_force_fields(bound.clone(), Some(&fields)).is_err()); + let empty = ForceScene::default().freeze().unwrap(); + assert!(inspector::verify_force_fields(&bound, Some(&empty)).is_err()); + assert_eq!(legacy.configuration, "literal-config"); +} +#[test] +fn force_retained_track_projection_is_readonly_and_clears_same_id_selection() { + let fields = scene().freeze().unwrap(); + let mut ui = scene_ui::SceneUi::geometry_fixture(ForceScene::default(), false); + ui.begin_run(false); + ui.bind_retained_fields(0, Some(&fields)).unwrap(); + ui.select(Some(SceneObjectId(1))); + assert_eq!(ui.selected(), Some(SceneObjectId(1))); + assert_eq!( + metadata(&ui.display_scene().freeze().unwrap()), + metadata(&fields) + ); + assert!(ui.selected_force().is_none()); + assert!(!ui.begin_edit()); + ui.bind_retained_fields(1, Some(&fields)).unwrap(); + assert_eq!(ui.selected(), None); + ui.select(Some(SceneObjectId(1))); + let mut changed = scene(); + changed.objects[0].name = "Other track document".into(); + ui.bind_retained_fields(1, Some(&changed.freeze().unwrap())) + .unwrap(); + assert_eq!(ui.selected(), None); + let ctx = egui::Context::default(); + ui.open = true; + let out = ctx.run(Default::default(), |ctx| ui.show(ctx, true, false, None)); + assert!( + out.shapes.iter().any( + |s| matches!(&s.shape,egui::Shape::Text(t)if t.galley.text().contains("read-only")) + ) + ); + assert!(ui.draft().objects.is_empty()); + ui.bind_retained_fields(2, None).unwrap(); + assert!(ui.display_scene().objects.is_empty()); +} +#[test] +fn force_retained_composition_resolves_once_before_tree_and_defers_track_intents() { + let mut request = request(); + request.methods = PressureImplementation::ALL.to_vec(); + let first = scene().freeze().unwrap(); + let mut second = scene(); + second.objects[0].name = "Second retained track".into(); + request.force_fields = vec![Some(first), Some(second.freeze().unwrap())]; + // Literal presentation data only: no capture, solver, worker or archive. + let history = request.preview_history().unwrap(); + for (track, method) in request.methods.iter().enumerate() { + history + .publish( + track, + Arc::new(PreviewFrame { + method: *method, + step: 0, + time: track as f64, + seconds: 0.0, + edge: 2, + nonzero_tracer_cells: 0, + density: vec![0; 8], + report: None, + particle_report: None, + material_color: None, + }), + ) + .unwrap(); + } + let mut app = App::default(); + app.running_request = Some(request); + app.history = Some(history); + app.scene.begin_run(false); + // The active track is 1 after literal publication; follow-live must resolve + // before binding tree/properties, never half way through painting. + let (_, frame) = app.prepare_scene_presentation().unwrap(); + assert_eq!(app.track, 1); + assert_eq!(frame.unwrap().time, 1.0); + assert_eq!( + app.scene.display_scene().objects[0].name, + "Second retained track" + ); + app.scene.select(Some(SceneObjectId(1))); + // This is the scalar intent committed by timeline painting. Both cached + // viewport and already composed properties stay on 1 until next prepare. + app.follow_live = false; + app.track = 0; + assert_eq!( + app.scene.display_scene().objects[0].name, + "Second retained track" + ); + let (_, frame) = app.prepare_scene_presentation().unwrap(); + assert_eq!(frame.unwrap().time, 0.0); + assert_eq!(app.scene.display_scene().objects[0].name, "Recorded force"); + assert_eq!(app.scene.selected(), None); + app.scene.context = scene_ui::SceneContext::Draft; + app.timeline_step = 9; + assert!(app.prepare_scene_presentation().is_none()); + assert_eq!(app.timeline_step, 9); + assert!(app.worker.is_none()); +} diff --git a/src/bin/desktop/inspector.rs b/src/bin/desktop/inspector.rs index f6e0b150..26041c77 100644 --- a/src/bin/desktop/inspector.rs +++ b/src/bin/desktop/inspector.rs @@ -6,11 +6,12 @@ use rheon::recording::provider::{ }; use rheon::recording::{ RecordedCarrier, RecordedMaterial, RecordedStep, RecordingLimits, RecordingReader, - StepRecorder, carrier_publication, carrier_run, material_publication, material_run, + StepRecorder, carrier_publication, carrier_publication_with_force_fields, carrier_run, + material_publication, material_publication_with_force_fields, material_run, }; use rheon::{ - FluidParticle, MaterialStepControls, ParticleFluidSimulation, ParticleStepReport, - SessionIdentity, Simulation, + FluidParticle, ForceSnapshot, MaterialStepControls, ParticleFluidSimulation, + ParticleStepReport, SessionIdentity, Simulation, }; use std::{ path::{Path, PathBuf}, @@ -132,15 +133,35 @@ impl Inspector { sim: &ParticleFluidSimulation, path: &Path, limits: RecordingLimits, + ) -> Result { + self.begin_with_force_fields(sim, path, limits, None) + } + pub fn begin_with_force_fields( + &self, + sim: &ParticleFluidSimulation, + path: &Path, + limits: RecordingLimits, + fields: Option<&ForceSnapshot>, ) -> Result { let run=material_run(super::identity::new_identity("material"),sim, "native desktop; caller-owned local path; source revision/build/toolchain not supplied; no restart guarantee") .map_err(|e|e.to_string())?; + let run = bind_force_fields(run, fields)?; let mut recorder = StepRecorder::create(path, run, limits).map_err(|e| e.to_string())?; recorder .append( - &material_publication(sim, None, None, 0, limits.max_record_bytes) - .map_err(|e| e.to_string())?, + &match fields { + Some(fields) => material_publication_with_force_fields( + sim, + None, + None, + fields, + 0, + limits.max_record_bytes, + ), + None => material_publication(sim, None, None, 0, limits.max_record_bytes), + } + .map_err(|e| e.to_string())?, ) .map_err(|e| e.to_string())?; self.reload(path, limits)?; @@ -152,10 +173,25 @@ impl Inspector { sim: &Simulation, path: &Path, limits: RecordingLimits, + ) -> Result { + self.begin_carrier_with_force_fields(sim, path, limits, None) + } + pub fn begin_carrier_with_force_fields( + &self, + sim: &Simulation, + path: &Path, + limits: RecordingLimits, + fields: Option<&ForceSnapshot>, ) -> Result { let run=carrier_run(super::identity::new_identity("carrier"),sim, "native desktop; source revision/build/toolchain not supplied; fixed sealed box; no restart guarantee").map_err(|e|e.to_string())?; - self.begin_bound_carrier(sim, path, limits, run) + self.begin_bound_carrier_with_force_fields( + sim, + path, + limits, + bind_force_fields(run, fields)?, + fields, + ) } pub fn begin_bound_carrier( &self, @@ -164,10 +200,30 @@ impl Inspector { limits: RecordingLimits, run: rheon::recording::RecordingRun, ) -> Result { + self.begin_bound_carrier_with_force_fields(sim, path, limits, run, None) + } + pub fn begin_bound_carrier_with_force_fields( + &self, + sim: &Simulation, + path: &Path, + limits: RecordingLimits, + run: rheon::recording::RecordingRun, + fields: Option<&ForceSnapshot>, + ) -> Result { + verify_force_fields(&run, fields)?; let mut recorder = StepRecorder::create(path, run, limits).map_err(|e| e.to_string())?; recorder .append( - &carrier_publication(sim, 0, limits.max_record_bytes).map_err(|e| e.to_string())?, + &match fields { + Some(fields) => carrier_publication_with_force_fields( + sim, + fields, + 0, + limits.max_record_bytes, + ), + None => carrier_publication(sim, 0, limits.max_record_bytes), + } + .map_err(|e| e.to_string())?, ) .map_err(|e| e.to_string())?; self.reload(path, limits)?; @@ -178,6 +234,14 @@ impl Inspector { &self, recorder: &mut StepRecorder, sim: &Simulation, + ) -> Result<(), String> { + self.record_carrier_with_force_fields(recorder, sim, None) + } + pub fn record_carrier_with_force_fields( + &self, + recorder: &mut StepRecorder, + sim: &Simulation, + fields: Option<&ForceSnapshot>, ) -> Result<(), String> { let actual = carrier_run( recorder.run().identity.clone(), @@ -185,14 +249,23 @@ impl Inspector { &recorder.run().provenance, ) .map_err(|e| e.to_string())?; + let actual = bind_force_fields(actual, fields)?; if &actual != recorder.run() { return Err("Carrier configuration/geometry changed; accepted step unrecorded".into()); } - let step = carrier_publication( - sim, - recorder.recorded_count(), - recorder.limits().max_record_bytes, - ) + let step = match fields { + Some(fields) => carrier_publication_with_force_fields( + sim, + fields, + recorder.recorded_count(), + recorder.limits().max_record_bytes, + ), + None => carrier_publication( + sim, + recorder.recorded_count(), + recorder.limits().max_record_bytes, + ), + } .map_err(|e| e.to_string())?; recorder.append(&step).map_err(|e| { format!( @@ -278,6 +351,16 @@ impl Inspector { sim: &ParticleFluidSimulation, report: ParticleStepReport, controls: MaterialStepControls, + ) -> Result<(), String> { + self.record_with_force_fields(recorder, sim, report, controls, None) + } + pub fn record_with_force_fields( + &self, + recorder: &mut StepRecorder, + sim: &ParticleFluidSimulation, + report: ParticleStepReport, + controls: MaterialStepControls, + fields: Option<&ForceSnapshot>, ) -> Result<(), String> { let actual = material_run( recorder.run().identity.clone(), @@ -285,18 +368,29 @@ impl Inspector { &recorder.run().provenance, ) .map_err(|e| e.to_string())?; + let actual = bind_force_fields(actual, fields)?; if &actual != recorder.run() { return Err( "Recording configuration/geometry changed; accepted step is unrecorded".into(), ); } - let step = material_publication( - sim, - Some(report), - Some(controls), - recorder.recorded_count(), - recorder.limits().max_record_bytes, - ) + let step = match fields { + Some(fields) => material_publication_with_force_fields( + sim, + Some(report), + Some(controls), + fields, + recorder.recorded_count(), + recorder.limits().max_record_bytes, + ), + None => material_publication( + sim, + Some(report), + Some(controls), + recorder.recorded_count(), + recorder.limits().max_record_bytes, + ), + } .map_err(|e| e.to_string())?; recorder.append(&step).map_err(|e| { format!( @@ -1068,3 +1162,28 @@ mod tests { } } } + +pub(super) fn bind_force_fields( + run: rheon::recording::RecordingRun, + fields: Option<&ForceSnapshot>, +) -> Result { + match fields { + Some(fields) => run.with_force_fields(fields).map_err(|e| e.to_string()), + None => Ok(run), + } +} +pub(super) fn verify_force_fields( + run: &rheon::recording::RecordingRun, + fields: Option<&ForceSnapshot>, +) -> Result<(), String> { + use sha2::{Digest, Sha256}; + let expected = fields + .map(rheon::recording::encode_force_metadata) + .transpose() + .map_err(|e| e.to_string())? + .map(|bytes| <[u8; 32]>::from(Sha256::digest(bytes))); + if run.force_fields_digest().map_err(|e| e.to_string())? != expected { + return Err("Recording force snapshot does not match immutable request".into()); + } + Ok(()) +} diff --git a/src/bin/desktop/native_presentation_harness.rs b/src/bin/desktop/native_presentation_harness.rs index 8094200a..853855e5 100644 --- a/src/bin/desktop/native_presentation_harness.rs +++ b/src/bin/desktop/native_presentation_harness.rs @@ -12,6 +12,9 @@ fn style(ctx: &egui::Context) { } fn fixture() -> App { let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 2, steps: 1, @@ -125,6 +128,7 @@ impl Presentation { self.app.play_time += self.app.playback_speed * f64::from(ctx.input(|i| i.stable_dt.min(0.1))); } + let retained_view = self.app.prepare_scene_presentation(); let previous = self.app.scene.context; self.app.scene.show(ctx, true, false, None); if previous != self.app.scene.context { @@ -132,19 +136,14 @@ impl Presentation { } let retained = self.app.scene.context == scene_ui::SceneContext::Retained; let (count, frame) = if retained { - self.app.selected_frame() + retained_view.unwrap_or_else(|| self.app.selected_frame_bound_track()) } else { (0, None) }; let timeline = self.app.scene_timeline_ui(ctx, count, frame.as_deref()); - let frame = if retained { - self.app.selected_frame().1 - } else { - None - }; self.app.viewport.domain_side = 1.0; self.app.viewport.authored_forces_pending = - !retained && !self.app.scene.draft().objects.is_empty(); + !self.app.scene.display_scene().objects.is_empty(); // Every frame resets to the fresh default Appearance owner. Disabled UI // prevents user dispatch into numerical labs; no production App::update. self.app.viewport.presentation_only_appearance(); diff --git a/src/bin/desktop/run_sets.rs b/src/bin/desktop/run_sets.rs index c8015ead..c23fcff2 100644 --- a/src/bin/desktop/run_sets.rs +++ b/src/bin/desktop/run_sets.rs @@ -26,6 +26,7 @@ use std::{ }; const MANIFEST_VERSION: u32 = 1; +const FORCE_SPEC_VERSION: u32 = 2; const MAX_MANIFEST_BYTES: u64 = 1024 * 1024; const MAX_MEMBERS: usize = 64; const MAX_TEXT_BYTES: usize = 1000; @@ -251,6 +252,12 @@ pub struct FrozenSpec { pub schema_version: u32, pub sha256: [u8; 32], pub spec: NativeCarrierSpec, + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_force_scene" + )] + pub force_fields: Option, } impl FrozenSpec { /// Freeze the existing native producer's actual constructor configuration. @@ -304,27 +311,92 @@ impl FrozenSpec { schema_version: MANIFEST_VERSION, sha256: digest(&spec)?, spec, + force_fields: None, }) } + pub fn with_force_fields(mut self, fields: &rheon::ForceSnapshot) -> Result { + self.validate()?; + if self.force_fields.is_some() { + return Err(RunSetError::Invalid("force intent already bound")); + } + self.schema_version = FORCE_SPEC_VERSION; + self.force_fields = Some(fields.scene().clone()); + self.sha256 = force_spec_digest(&self.spec, fields)?; + Ok(self) + } + pub fn force_snapshot(&self) -> Result, RunSetError> { + self.force_fields + .as_ref() + .map(|scene| { + scene + .freeze() + .map_err(|_| RunSetError::Invalid("frozen force document")) + }) + .transpose() + } pub fn validate(&self) -> Result<(), RunSetError> { - version(self.schema_version)?; self.spec.validate()?; - if self.sha256 != digest(&self.spec)? { + let expected = match (self.schema_version, self.force_fields.as_ref()) { + (MANIFEST_VERSION, None) => digest(&self.spec)?, + (FORCE_SPEC_VERSION, Some(_)) => force_spec_digest( + &self.spec, + &self + .force_snapshot()? + .ok_or(RunSetError::Invalid("force intent missing"))?, + )?, + (MANIFEST_VERSION | FORCE_SPEC_VERSION, _) => { + return Err(RunSetError::Invalid("frozen intent version/payload")); + } + _ => return Err(RunSetError::Unsupported("frozen native intent version")), + }; + if self.sha256 != expected { return Err(RunSetError::Integrity("spec digest")); } Ok(()) } + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() as u64 > MAX_MANIFEST_BYTES { + return Err(RunSetError::Invalid("manifest byte limit")); + } + let result: Self = serde_json::from_slice(bytes)?; + result.validate()?; + Ok(result) + } pub fn save_new(&self, path: &Path) -> Result<(), RunSetError> { self.validate()?; publish_new(path, &serde_json::to_vec(self)?, MAX_MANIFEST_BYTES) } pub fn open(path: &Path) -> Result { - let result: Self = read_versioned(path, MANIFEST_VERSION, MAX_MANIFEST_BYTES)?; - result.validate()?; - Ok(result) + Self::from_bytes(&read_document(path, MAX_MANIFEST_BYTES)?) } } +fn deserialize_force_scene<'de, D: serde::Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + rheon::ForceScene::deserialize(deserializer).map(Some) +} +fn force_spec_digest( + spec: &NativeCarrierSpec, + fields: &rheon::ForceSnapshot, +) -> Result<[u8; 32], RunSetError> { + let spec = serde_json::to_vec(spec)?; + let fields = rheon::recording::encode_force_metadata(fields)?; + let mut hash = Sha256::new(); + hash.update(b"rheon-desktop-native-force-spec-v2\0"); + hash.update((spec.len() as u64).to_le_bytes()); + hash.update(spec); + hash.update((fields.len() as u64).to_le_bytes()); + hash.update(fields); + Ok(hash.finalize().into()) +} +fn force_bytes(spec: &FrozenSpec) -> Result>, RunSetError> { + spec.force_snapshot()? + .as_ref() + .map(rheon::recording::encode_force_metadata) + .transpose() + .map_err(Into::into) +} #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] struct NativeIntentDeclaration { @@ -367,11 +439,15 @@ pub fn bound_native_run( return Err(RunSetError::Invalid("native constructor/spec binding")); } let provenance = serde_json::to_string(&NativeIntentDeclaration { - schema_version: MANIFEST_VERSION, + schema_version: spec.schema_version, spec_sha256: spec.sha256, build_provenance: build_provenance.into(), })?; - Ok(carrier_run(identity, simulation, &provenance)?) + let run = carrier_run(identity, simulation, &provenance)?; + Ok(match spec.force_snapshot()? { + Some(fields) => run.with_force_fields(&fields)?, + None => run, + }) } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] @@ -455,6 +531,7 @@ pub enum SpecDifference { Source, MemoryBudget, RecordingBudget, + ForceMetadata, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(deny_unknown_fields)] @@ -496,7 +573,11 @@ impl RunSetRevision { { return Err(RunSetError::Invalid("duplicate member")); } - if differences(&baseline.spec.spec, &m.spec.spec) != m.differences_from_baseline { + let mut declared = differences(&baseline.spec.spec, &m.spec.spec); + if force_bytes(&baseline.spec)? != force_bytes(&m.spec)? { + declared.push(SpecDifference::ForceMetadata); + } + if declared != m.differences_from_baseline { return Err(RunSetError::Invalid("declared differences")); } if let Some(a) = &m.attempt { @@ -633,7 +714,13 @@ fn validate_binding( return Err(RunSetError::Unsupported("saved native carrier profile")); } let declaration: NativeIntentDeclaration = serde_json::from_str(&run.provenance)?; - version(declaration.schema_version)?; + if declaration.schema_version != spec.schema_version { + return Err(RunSetError::Integrity("archive intent version")); + } + let expected_force = force_bytes(spec)?.map(|bytes| <[u8; 32]>::from(Sha256::digest(bytes))); + if run.force_fields_digest()? != expected_force { + return Err(RunSetError::Integrity("archive force intent")); + } text(&declaration.build_provenance)?; if run.identity.run_id != a.expected_run_id || run.identity.source_revision != a.expected_source_revision @@ -786,6 +873,20 @@ pub(crate) fn read_versioned( expected_version: u32, max_bytes: u64, ) -> Result { + let bytes = read_document(path, max_bytes)?; + #[derive(Deserialize)] + struct Header { + schema_version: u64, + } + let header: Header = serde_json::from_slice(&bytes)?; + if header.schema_version != u64::from(expected_version) { + return Err(RunSetError::Unsupported("manifest version")); + } + // Decode the original bytes so duplicate required keys are not erased by a + // generic JSON object representation before strict typed validation. + Ok(serde_json::from_slice(&bytes)?) +} +fn read_document(path: &Path, max_bytes: u64) -> Result, RunSetError> { if max_bytes == 0 || max_bytes == u64::MAX { return Err(RunSetError::Invalid("document byte limit")); } @@ -803,17 +904,7 @@ pub(crate) fn read_versioned( if bytes.len() as u64 > max_bytes { return Err(RunSetError::Invalid("manifest byte limit")); } - #[derive(Deserialize)] - struct Header { - schema_version: u64, - } - let header: Header = serde_json::from_slice(&bytes)?; - if header.schema_version != u64::from(expected_version) { - return Err(RunSetError::Unsupported("manifest version")); - } - // Decode the original bytes so duplicate required keys are not erased by a - // generic JSON object representation before strict typed validation. - Ok(serde_json::from_slice(&bytes)?) + Ok(bytes) } /// Atomic no-replace publication on caller-owned local filesystems supporting /// hard links and directory sync. Protect every path component from concurrent diff --git a/src/bin/desktop/run_sets/source.rs b/src/bin/desktop/run_sets/source.rs index 92157f35..1a2a41ba 100644 --- a/src/bin/desktop/run_sets/source.rs +++ b/src/bin/desktop/run_sets/source.rs @@ -205,6 +205,7 @@ impl<'a> SavedMember<'a> { SpecDifference::TimePolicy => "Requested intervals", SpecDifference::Source => "Smoke source", SpecDifference::MemoryBudget => "Simulation memory limit", + SpecDifference::ForceMetadata => "Force metadata", SpecDifference::RecordingBudget => "Recording budget", }) .collect(), diff --git a/src/bin/desktop/scene_handles.rs b/src/bin/desktop/scene_handles.rs index 4ce64b5e..0a80228d 100644 --- a/src/bin/desktop/scene_handles.rs +++ b/src/bin/desktop/scene_handles.rs @@ -492,7 +492,7 @@ impl SceneHandles { scene: &SceneUi, editable: bool, ) { - if !editable { + if !editable || scene.context != super::scene_ui::SceneContext::Draft { return; } let display = scene.display_scene(); diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index a0b5b692..9634c4d0 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -37,6 +37,8 @@ struct DraftEdit { valid: bool, } pub struct SceneUi { + retained_forces: Option, + retained_binding: Option<(usize, Option<[u8; 32]>)>, edit: Option, forces: ForceScene, selected: Option, @@ -50,6 +52,8 @@ pub struct SceneUi { impl Default for SceneUi { fn default() -> Self { Self { + retained_forces: None, + retained_binding: None, edit: None, forces: ForceScene::default(), selected: None, @@ -75,6 +79,33 @@ impl SceneUi { pub fn draft(&self) -> &ForceScene { &self.forces } + pub fn freeze_for_run(&self) -> Result, String> { + if self.forces.objects.is_empty() { + Ok(None) + } else { + self.forces.freeze().map(Some).map_err(|e| e.to_string()) + } + } + pub fn bind_retained_fields( + &mut self, + track: usize, + fields: Option<&rheon::ForceSnapshot>, + ) -> Result<(), String> { + use sha2::{Digest, Sha256}; + let identity = fields + .map(rheon::recording::encode_force_metadata) + .transpose() + .map_err(|e| e.to_string())? + .map(|bytes| <[u8; 32]>::from(Sha256::digest(bytes))); + if self.retained_binding != Some((track, identity)) { + if self.context == SceneContext::Retained { + self.selected = None; + } + self.retained_binding = Some((track, identity)); + self.retained_forces = fields.map(|f| f.scene().clone()); + } + Ok(()) + } pub fn selected_force(&self) -> Option<&ForceObject> { (self.context == SceneContext::Draft) .then(|| { @@ -89,6 +120,12 @@ impl SceneUi { self.edit.is_some() } pub fn display_scene(&self) -> std::borrow::Cow<'_, ForceScene> { + if self.context == SceneContext::Retained { + return self.retained_forces.as_ref().map_or_else( + || std::borrow::Cow::Owned(ForceScene::default()), + std::borrow::Cow::Borrowed, + ); + } if let Some(edit) = &self.edit && self.edit_matches() { @@ -207,11 +244,15 @@ impl SceneUi { Ok(()) } pub fn begin_run(&mut self, model_present: bool) { + self.retained_binding = None; + self.retained_forces = None; self.selected = None; self.retained_model_id = if model_present { self.model_id } else { None }; self.set_context(SceneContext::Retained); } pub fn clear_run(&mut self) { + self.retained_binding = None; + self.retained_forces = None; self.retained_model_id = None; self.set_context(SceneContext::Draft); } @@ -224,9 +265,7 @@ impl SceneUi { /// This is the sole selection owner. Viewport picking will call this same API. pub fn select(&mut self, id: Option) { self.selected = id.filter(|id| { - Some(*id) == self.model_id() - || (self.context == SceneContext::Draft - && self.forces.objects.iter().any(|o| o.id == *id)) + Some(*id) == self.model_id() || self.display_scene().objects.iter().any(|o| o.id == *id) }); } pub fn launch_readiness(&self) -> Result<(), String> { @@ -324,6 +363,10 @@ impl SceneUi { } }); ui.small("Gravity remains additive.").on_hover_text("Acceleration fields add to configured gravity. Avoid applying Earth gravity twice. Force density divides once by local mass density, never particle mass."); + } else if let Some(scene)=&self.retained_forces { + ui.small(format!("Retained track {} · read-only",self.retained_binding.map_or(1,|b|b.0+1))); + for object in &scene.objects {if ui.push_id(("retained-force",object.id.0),|ui|ui.selectable_label(self.selected==Some(object.id),&object.name)).inner.clicked(){self.selected=Some(object.id);}} + if scene.objects.is_empty(){ui.label("No authored forces");} } else {ui.label("Authored-force metadata unavailable").on_hover_text("This legacy retained run has no canonical authored-force document. Its physics is not inferred from the current draft.");} ui.separator(); ui.heading("Properties"); @@ -339,6 +382,8 @@ impl SceneUi { if changed {self.validate();} if ui.add_enabled(editable,egui::Button::new("Remove force")).clicked(){self.remove_selected();} } else {ui.label("Select an object");} + } else if let Some(object)=self.retained_forces.as_ref().and_then(|scene|scene.objects.iter().find(|o|Some(o.id)==self.selected)) { + let mut readonly=object.clone();ui.push_id(("retained-properties",object.id.0),|ui|{ui.add_enabled_ui(false,|ui|force_properties(ui,&mut readonly));}); } else {ui.label("Select an object");} }); }); diff --git a/src/bin/desktop/scene_view.rs b/src/bin/desktop/scene_view.rs index ced3a51a..42608280 100644 --- a/src/bin/desktop/scene_view.rs +++ b/src/bin/desktop/scene_view.rs @@ -1,8 +1,5 @@ //! Visual authoring geometry only: no field evaluation, solver or model loading. -use super::{ - camera::OrbitCamera, - scene_ui::{SceneContext, SceneUi}, -}; +use super::{camera::OrbitCamera, scene_ui::SceneUi}; use eframe::egui::{self, Color32, Pos2, Rect, Stroke}; use rheon::{ForceKind, ForceObject, ForceSupport, SceneObjectId}; use std::collections::BTreeMap; @@ -364,7 +361,7 @@ impl Projection { return result; } let display_scene = state.display_scene(); - if state.context == SceneContext::Draft { + { if display_scene.freeze().is_err() { result.warning = Some("Invalid force draft · fix properties to preview"); result.pick_incomplete = true; diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index cb63d636..3c54d610 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -371,6 +371,9 @@ fn ui_setup_running_summary_uses_frozen_physical_samples_and_timestep() { ..Default::default() }; let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 32, steps: 37, @@ -621,6 +624,9 @@ fn ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_sign app.steps = 99; app.requested_dt = 0.001; app.running_request = Some(Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 4, steps: 4, @@ -757,6 +763,9 @@ fn bounded_genuine_cancel_contract() { let (reached, gate) = std::sync::mpsc::sync_channel(1); let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 4, steps: 2, diff --git a/src/bin/desktop/timeline_ui_tests.rs b/src/bin/desktop/timeline_ui_tests.rs index 0a8317c0..e462dda1 100644 --- a/src/bin/desktop/timeline_ui_tests.rs +++ b/src/bin/desktop/timeline_ui_tests.rs @@ -247,6 +247,9 @@ fn timeline_controls_retain_transitions_and_physical_time() { // Pure values only. No Worker, history, mesh, file, process or numerical owner. fn inert_carrier_request() -> Request { Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 4, steps: 4, diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index 621779d2..e1e518d6 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -53,6 +53,9 @@ pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; #[cfg(test)] +#[path = "desktop/force_runtime_ui_tests.rs"] +mod force_runtime_ui_tests; +#[cfg(test)] #[path = "desktop/native_presentation_harness.rs"] mod native_presentation_harness; #[path = "desktop/scene_handles.rs"] @@ -88,6 +91,10 @@ use std::time::{Duration, Instant}; #[derive(Clone)] struct Request { + /// Empty means legacy on every track; otherwise one immutable option per track. + force_fields: Vec>, + #[cfg(test)] + force_validation: bool, collision_mesh: Option>, size: u64, steps: usize, @@ -122,6 +129,10 @@ struct ConfiguredRequest { runtime: Option<(Arc, accepted_session::model_process::ChildSlot)>, } impl Request { + fn fields_for_track(&self, track: usize) -> Option<&rheon::ForceSnapshot> { + self.force_fields.get(track).and_then(Option::as_ref) + } + fn setup_workflow(&self) -> SetupWorkflow { if self.configured_set.is_some() { SetupWorkflow::AcceptedSession @@ -258,6 +269,47 @@ fn configured_executor() -> run_sets::configured::ExecutorDeclaration { } } fn validate_request(request: &Request) -> Result<(), String> { + if !request.force_fields.is_empty() && request.force_fields.len() != request.methods.len() { + return Err("Force snapshots must match the exact track count".into()); + } + for fields in request.force_fields.iter().flatten() { + rheon::recording::encode_force_metadata(fields).map_err(|e| e.to_string())?; + } + let authored = request.force_fields.iter().any(Option::is_some); + if authored && request.material.is_some() && request.methods.len() != 1 { + return Err("Authored particle material requires exactly one material track".into()); + } + if authored && request.configured_set.is_some() { + return Err("Authored forces are unsupported for configured/session/provider/free-surface execution".into()); + } + if let Some(set) = &request.saved_set { + set.validate().map_err(|e| e.to_string())?; + if set.members.len() != request.methods.len() { + return Err("Saved member/track mismatch".into()); + } + for (track, member) in set.members.iter().enumerate() { + let intended = member.spec.force_snapshot().map_err(|e| e.to_string())?; + let bytes = |f: Option<&rheon::ForceSnapshot>| { + f.map(rheon::recording::encode_force_metadata) + .transpose() + .map_err(|e| e.to_string()) + }; + if bytes(intended.as_ref())? != bytes(request.fields_for_track(track))? { + return Err("Saved member force snapshot mismatch".into()); + } + } + } + #[cfg(test)] + let force_admitted = request.force_validation; + #[cfg(not(test))] + let force_admitted = false; + if authored && !force_admitted { + return Err( + "Authored forces cannot run yet: bounded desktop runtime/archive validation is pending" + .into(), + ); + } + if let Some(configured) = &request.configured_set { configured.revision.validate().map_err(|e| e.to_string())?; if request.saved_set.is_some() @@ -535,11 +587,32 @@ fn run_simulations( }); set.parent_sha256 = Some(parent); publish_saved_revision(&request, set, &format!("member-{track}-started.json"))?; - inspector.begin_bound_carrier(sim.native()?, &path, request.recording_limits, run)? + match request.fields_for_track(track) { + Some(fields) => inspector.begin_bound_carrier_with_force_fields( + sim.native()?, + &path, + request.recording_limits, + run, + Some(fields), + ), + None => inspector.begin_bound_carrier( + sim.native()?, + &path, + request.recording_limits, + run, + ), + }? } else { - inspector - .begin_carrier(sim.native()?, &path, request.recording_limits) - .map_err(|e| format!("Carrier recording start at {} failed: {e}", path.display()))? + match request.fields_for_track(track) { + Some(fields) => inspector.begin_carrier_with_force_fields( + sim.native()?, + &path, + request.recording_limits, + Some(fields), + ), + None => inspector.begin_carrier(sim.native()?, &path, request.recording_limits), + } + .map_err(|e| format!("Carrier recording start at {} failed: {e}", path.display()))? }; use rheon::recording::provider::runtime::{RuntimeTermination, RuntimeTerminationCause}; let mut termination_cause = RuntimeTerminationCause::StorageFailure; @@ -589,6 +662,7 @@ fn run_simulations( |spec| spec.spec.source_at(step), ), cancel, + request.fields_for_track(track), ); let step_seconds = start.elapsed().as_secs_f64(); history.end_step()?; @@ -604,7 +678,14 @@ fn run_simulations( configured.runtime_observation(), )?; } else { - inspector.record_carrier(&mut recorder, sim.native()?)?; + match request.fields_for_track(track) { + Some(fields) => inspector.record_carrier_with_force_fields( + &mut recorder, + sim.native()?, + Some(fields), + ), + None => inspector.record_carrier(&mut recorder, sim.native()?), + }?; } termination_cause = RuntimeTerminationCause::NumericalFailure; if frozen.is_some() && r.dt.to_bits() != request.requested_dt.to_bits() { @@ -842,6 +923,10 @@ fn freeze_native_set(request: &mut Request) -> Result spec.with_force_fields(fields).map_err(|e| e.to_string())?, + None => spec, + }; members.push(RunSetMember { member_id: format!("native-{track}"), spec, @@ -914,10 +999,19 @@ fn run_material( sim.set_collision_mesh(request.collision_mesh.clone()) .map_err(|e| e.to_string())?; let path = new_recording_path(&request.recording_directory, "material")?; - let mut recorder = history - .inspector - .begin(&sim, &path, request.recording_limits) - .map_err(|e| format!("Recording start at {} failed: {e}", path.display()))?; + let fields = request.fields_for_track(0); + let mut recorder = match fields { + Some(fields) => history.inspector.begin_with_force_fields( + &sim, + &path, + request.recording_limits, + Some(fields), + ), + None => history + .inspector + .begin(&sim, &path, request.recording_limits), + } + .map_err(|e| format!("Recording start at {} failed: {e}", path.display()))?; let result = (|| { history.publish( 0, @@ -936,29 +1030,44 @@ fn run_material( for step in 0..request.steps { history.begin_step(0, step + 1)?; let start = Instant::now(); - let result = sim.step(request.requested_dt, request.emitter, || { - cancel.load(Ordering::Relaxed) - }); + let result = match fields { + Some(fields) => sim.step_with_force_fields( + request.requested_dt, + request.emitter, + fields, + || cancel.load(Ordering::Relaxed), + ), + None => sim.step(request.requested_dt, request.emitter, || { + cancel.load(Ordering::Relaxed) + }), + }; let elapsed = start.elapsed().as_secs_f64(); history.end_step()?; let report = result.map_err(|e| e.to_string())?; seconds += elapsed; last_report = Some(report); - history.inspector.record( - &mut recorder, - &sim, - report, - rheon::MaterialStepControls { - requested_dt: request.requested_dt, - emitter: request - .emitter - .map(|emitter| rheon::MaterialEmitterControl { - source_id: 1, - source_version: 1, - emitter, - }), - }, - )?; + let controls = rheon::MaterialStepControls { + requested_dt: request.requested_dt, + emitter: request + .emitter + .map(|emitter| rheon::MaterialEmitterControl { + source_id: 1, + source_version: 1, + emitter, + }), + }; + match fields { + Some(fields) => history.inspector.record_with_force_fields( + &mut recorder, + &sim, + report, + controls, + Some(fields), + ), + None => history + .inspector + .record(&mut recorder, &sim, report, controls), + }?; history.publish( 0, PreviewFrame::capture_particles( @@ -1639,6 +1748,9 @@ impl App { let baseline = &revision.members[0].spec; let intent_path = root.join("intent.json"); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: baseline.constructor.counts_xyz[0], steps: baseline.future.requested_steps, @@ -1784,7 +1896,19 @@ impl App { return; } }; + let fields = match self.scene.freeze_for_run() { + Ok(fields) => fields, + Err(error) => { + self.status = error; + return; + } + }; let mut request = Request { + force_fields: fields.map_or_else(Vec::new, |fields| { + vec![Some(fields); if all { 2 } else { 1 }] + }), + #[cfg(test)] + force_validation: false, collision_mesh: self.collision.mesh.clone(), size, steps: self.steps, @@ -1849,6 +1973,9 @@ impl App { return Err("Desktop rerun supports up to two cubic zero-origin members with common geometry, requested horizon and budgets; cold inspection remains available".into()); } let mut request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: baseline.counts_xyz[0], steps: baseline.requested_steps, @@ -1882,6 +2009,11 @@ impl App { #[cfg(test)] failure_gate: None, }; + request.force_fields = original + .members + .iter() + .map(|m| m.spec.force_snapshot().map_err(|e| e.to_string())) + .collect::, _>>()?; validate_request(&request)?; let root = new_recording_path(&request.recording_directory, "rerun")?.with_extension("runset"); @@ -1911,6 +2043,9 @@ impl App { self.history = Some(Arc::clone(&worker.history)); self.worker = Some(worker); self.scene.begin_run(request.collision_mesh.is_some()); + let retained_binding = self + .scene + .bind_retained_fields(0, request.fields_for_track(0)); self.running_request = Some(request); self.rows.clear(); self.textures.clear(); @@ -1918,7 +2053,10 @@ impl App { self.track = 0; self.follow_live = true; self.playing = false; - self.status = "Simulating — accepted steps appear live.".into(); + self.status = retained_binding.map_or_else( + |error| format!("Running; retained force display unavailable: {error}"), + |()| "Simulating — accepted steps appear live.".into(), + ); Ok(()) } Err(e) => { @@ -1984,11 +2122,34 @@ impl App { } } } + /// One render-frame snapshot of track, appearance and force metadata. Track + /// intents painted later apply visually next frame, never to half a frame. + fn prepare_scene_presentation(&mut self) -> Option<(usize, Option>)> { + let view = + (self.scene.context == scene_ui::SceneContext::Retained).then(|| self.selected_frame()); + if let Some(request) = &self.running_request + && let Err(error) = self + .scene + .bind_retained_fields(self.track, request.fields_for_track(self.track)) + { + self.status = error; + } + view + } fn selected_frame(&mut self) -> (usize, Option>) { + self.selected_frame_inner(true) + } + fn selected_frame_bound_track(&mut self) -> (usize, Option>) { + self.selected_frame_inner(false) + } + fn selected_frame_inner( + &mut self, + resolve_active_track: bool, + ) -> (usize, Option>) { let Some(history) = &self.history else { return (0, None); }; - if self.follow_live { + if self.follow_live && resolve_active_track { self.track = history.active_track.load(Ordering::Relaxed); } if self.playing { @@ -2441,6 +2602,7 @@ impl eframe::App for App { ctx.request_repaint(); return; } + let retained_view = self.prepare_scene_presentation(); let model_mesh = if self.scene.context == scene_ui::SceneContext::Retained { self.running_request .as_ref() @@ -2464,7 +2626,7 @@ impl eframe::App for App { } let retained = self.scene.context == scene_ui::SceneContext::Retained; let (count, selected) = if retained { - self.selected_frame() + retained_view.unwrap_or_else(|| self.selected_frame_bound_track()) } else { (0, None) }; @@ -2507,12 +2669,10 @@ impl eframe::App for App { .as_ref() .filter(|_| retained) .map_or(self.size, |r| r.size) as usize; - self.viewport.authored_forces_pending = !retained && !self.scene.draft().objects.is_empty(); - let frame = if retained { - self.selected_frame().1 - } else { - None - }; + self.viewport.authored_forces_pending = !self.scene.display_scene().objects.is_empty(); + let frame = selected; + let editable_scene = + self.worker.is_none() && self.scene.context == scene_ui::SceneContext::Draft; egui::CentralPanel::default() .frame(egui::Frame::NONE) .show(ctx, |ui| { @@ -2529,7 +2689,7 @@ impl eframe::App for App { .filter(|_| retained) .map(|h| &h.inspector), scene: Some(&mut self.scene), - editable_scene: self.worker.is_none(), + editable_scene, }, ) }); @@ -3022,6 +3182,9 @@ mod tests { #[test] fn material_worker_accepts_and_captures_a_genuine_256_cube_preview() { let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 256, steps: 1, @@ -3241,6 +3404,9 @@ mod tests { let (reached, first_step) = std::sync::mpsc::sync_channel(1); let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 32, steps: 64, @@ -3302,6 +3468,9 @@ mod tests { let (reached, first_step) = std::sync::mpsc::sync_channel(1); let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 32, steps: 3, @@ -3389,6 +3558,9 @@ mod tests { let (reached, first_step) = std::sync::mpsc::sync_channel(1); let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: app.size, steps: app.steps, @@ -3476,6 +3648,9 @@ mod tests { #[test] fn worker_runs_selected_and_equal_input_comparison() { let request = Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 8, steps: 4, @@ -3573,6 +3748,9 @@ mod tests { assert!( run( Request { + force_fields: Vec::new(), + #[cfg(test)] + force_validation: false, collision_mesh: None, size: 8, steps: 4, From d6e23b36ab618f8873d32c89c67a62e95f775c5b Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:18:28 +0000 Subject: [PATCH 15/25] Keep scaled timeline selectors and captions within row bounds --- .../timeline-scaled-20261021/.gitattributes | 1 + .../timeline-scaled-20261021/manifest.json | 99 +++++++++ .../rheon-ui-timeline-scaled-after1.log | 8 + .../rheon-ui-timeline-scaled-baseline.log | 21 ++ .../rheon-ui-timeline-scaled-before.log | 21 ++ .../rheon-ui-timeline-scaled-check.log | 2 + .../rheon-ui-timeline-scaled-clippy.log | 2 + .../rheon-ui-timeline-scaled-final1.log | 8 + .../rheon-ui-timeline-scaled-final2.log | 8 + .../rheon-ui-timeline-scaled-final3.log | 8 + .../rheon-ui-timeline-scaled-final4.log | 8 + .../rheon-ui-timeline-scaled-final5.log | 8 + .../rheon-ui-timeline-scaled-fmt-final.log | 0 .../rheon-ui-timeline-scaled-fmt.log | 0 .../rheon-ui-timeline-scaled-list1.log | 203 ++++++++++++++++++ .../rheon-ui-timeline-scaled-list2.log | 203 ++++++++++++++++++ .../rheon-ui-timeline-scaled-list3.log | 203 ++++++++++++++++++ .../rheon-ui-timeline-scaled-list4.log | 203 ++++++++++++++++++ .../rheon-ui-timeline-scaled-list5.log | 203 ++++++++++++++++++ docs/plans/ui-refinement/execution-ledger.md | 15 ++ .../reports/timeline-scaled-checkpoint.md | 38 ++++ .../reports/timeline-scaled-source-review.md | 44 ++++ src/bin/desktop/timeline_ui_tests.rs | 52 +++++ src/bin/rheon-desktop.rs | 92 +++++--- 24 files changed, 1423 insertions(+), 27 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-after1.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-baseline.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-before.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-check.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final1.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final2.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final3.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final4.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final5.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt-final.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list1.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list2.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list3.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list4.log create mode 100644 docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list5.log create mode 100644 docs/plans/ui-refinement/reports/timeline-scaled-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/timeline-scaled-source-review.md diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/.gitattributes b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/manifest.json b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/manifest.json new file mode 100644 index 00000000..820893d6 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/manifest.json @@ -0,0 +1,99 @@ +{ + "base": "5eab3ac4b6d4005ae77b145b3d970b9692debd4c", + "test_enumeration": 198, + "source_seals": { + "src/bin/rheon-desktop.rs": "a704b5dbb9978449187346f4b6eb8f85a9769c96a3f8ddfe85a5dd98f2766282", + "src/bin/desktop/timeline_ui_tests.rs": "606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b" + }, + "exact_final_passes": 5, + "baseline": "true clipping failure in baseline.log; before.log is test setup scheduled-zoom failure", + "logs": [ + { + "file": "rheon-ui-timeline-scaled-after1.log", + "bytes": 405, + "sha256": "6f448a085f5e0583440c572ce89960575e150ac2279ea2b72773c3a62810098b" + }, + { + "file": "rheon-ui-timeline-scaled-baseline.log", + "bytes": 1065, + "sha256": "b79ee82d68c555c82b0af22a969f98f1181c6c0d39dd30a2cdc8d51ac7f83e6b" + }, + { + "file": "rheon-ui-timeline-scaled-before.log", + "bytes": 971, + "sha256": "7ddb5367499dbbddac573e0cdc0a0a38a44ed272206a7de753bb20956704bbaf" + }, + { + "file": "rheon-ui-timeline-scaled-check.log", + "bytes": 130, + "sha256": "e05b12393294b621219310fbe0c6f78c2fed1de17b2de9a5dd62ab70a72bd6af" + }, + { + "file": "rheon-ui-timeline-scaled-clippy.log", + "bytes": 130, + "sha256": "711c3f4cbf3d881bb66a345d5cde56e752c9a8105da188e08b3b0e6a6930aab4" + }, + { + "file": "rheon-ui-timeline-scaled-final1.log", + "bytes": 405, + "sha256": "f01347d6eefe87fa31050258970697f24af720ad3250860c72c928dc1bf1b997" + }, + { + "file": "rheon-ui-timeline-scaled-final2.log", + "bytes": 395, + "sha256": "c428d9d1963816f3a1013e594b36913c04baf5904fb7caac75fc88f61b996ac9" + }, + { + "file": "rheon-ui-timeline-scaled-final3.log", + "bytes": 396, + "sha256": "9ed53949650886d830b08860e1d16dd322df3b472cc0cef332afc44d5a2a062c" + }, + { + "file": "rheon-ui-timeline-scaled-final4.log", + "bytes": 398, + "sha256": "cf5c8b61d76e58edbb876344086a71b2f8226a6290627d8fd8f8926c2718f04f" + }, + { + "file": "rheon-ui-timeline-scaled-final5.log", + "bytes": 410, + "sha256": "e7071fb5345d93542b973a8d043aa57ea8d3f406157c933a283d40bd59432846" + }, + { + "file": "rheon-ui-timeline-scaled-fmt-final.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-timeline-scaled-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-timeline-scaled-list1.log", + "bytes": 20892, + "sha256": "539557aebcc07021c8b742b1e90b451c38cea9d8b9f63d3b817cc9ecbc80e151" + }, + { + "file": "rheon-ui-timeline-scaled-list2.log", + "bytes": 20892, + "sha256": "b76db6304dc9bd25e017997ced858db0af8bba086b7dfa23c317aea62b642e38" + }, + { + "file": "rheon-ui-timeline-scaled-list3.log", + "bytes": 20892, + "sha256": "fcc635807972bbb841f1567f491cad05e716df309295103eef073fa19bc2f5f3" + }, + { + "file": "rheon-ui-timeline-scaled-list4.log", + "bytes": 20892, + "sha256": "cca8da2d09bde215c1669ed56a99da8feac810dea9dffce7f840fef86a79d919" + }, + { + "file": "rheon-ui-timeline-scaled-list5.log", + "bytes": 20892, + "sha256": "d1f1e2c4eca3333d484fb94b8583261595571af0d03c767bdc1724cc9dc592e8" + } + ], + "native_verified": false, + "numerical_execution": false +} diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-after1.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-after1.log new file mode 100644 index 00000000..cda04a03 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-after1.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-baseline.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-baseline.log new file mode 100644 index 00000000..1a6b1a87 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-baseline.log @@ -0,0 +1,21 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip ... FAILED + +failures: + +---- timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip stdout ---- + +thread 'timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip' (56691) panicked at src/bin/desktop/timeline_ui_tests.rs:117:13: +clipped timeline text: jacobi-pcg-v1 at [[498.0 349.5] - [573.3 364.2]], panel [[280.0 320.3] - [603.3 433.3]], clip [[280.0 320.3] - [536.0 433.3]] +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.01s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-before.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-before.log new file mode 100644 index 00000000..55bc48d8 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-before.log @@ -0,0 +1,21 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip ... FAILED + +failures: + +---- timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip stdout ---- + +thread 'timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip' (56302) panicked at src/bin/desktop/timeline_ui_tests.rs:71:5: +panel outside bounds: [[280.0 6593.7] - [6602.7 6666.7]] +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.01s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-check.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-check.log new file mode 100644 index 00000000..b76e79db --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.15s diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-clippy.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-clippy.log new file mode 100644 index 00000000..e7e00645 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.82s diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final1.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final1.log new file mode 100644 index 00000000..278ed60e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final1.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.02s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final2.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final2.log new file mode 100644 index 00000000..7cf5434c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final3.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final3.log new file mode 100644 index 00000000..47785664 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final3.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.05s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final4.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final4.log new file mode 100644 index 00000000..998119e9 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final4.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.05s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final5.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final5.log new file mode 100644 index 00000000..4e9daede --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-final5.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 197 filtered out; finished in 0.02s + diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt-final.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt-final.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list1.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list1.log new file mode 100644 index 00000000..93c41a9b --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list1.log @@ -0,0 +1,203 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 8.23s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +198 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list2.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list2.log new file mode 100644 index 00000000..380aaba2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list2.log @@ -0,0 +1,203 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 6.32s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +198 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list3.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list3.log new file mode 100644 index 00000000..8440ced1 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list3.log @@ -0,0 +1,203 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 7.88s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +198 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list4.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list4.log new file mode 100644 index 00000000..f391f0a4 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list4.log @@ -0,0 +1,203 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 8.17s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +198 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list5.log b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list5.log new file mode 100644 index 00000000..9114ab56 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/timeline-scaled-20261021/rheon-ui-timeline-scaled-list5.log @@ -0,0 +1,203 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 4.76s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +198 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index aca4f64e..fe953b6d 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -485,3 +485,18 @@ is admitted. Production launch gate stays; runtime/archive parity, genuine Cance long-status/accessibility/full M1 and T5 native acceptance are unqualified. No native image/Library claim; existing8148fa2 validator continues independently. Preserve all earlier captures, seals, branches, incident evidence and disposable outputs. + +## Narrow scaled timeline repair —2026-10-21 + +Separate after5eab3ac. Parent's exact8148fa2 native synthetic playback checks passed, +but900×650/150% track/time clipping was reported. Two production initial-frame +stalls leave picking/outline/handles/Escape/camera/property synchronization UNVERIFIED; +existing validator diagnoses them, no duplicate session. Added corrected native-scale +literal UI probe with setup280/collapsedScene64 geometry. It reproduced real track +clip before fix; preserve initial scheduled-zoom test-setup failure separately. +Bound full-row selected galley/ComboBox decoration, explicitly wrap whole control; +prewrap captions full-row without font/state/ID changes. Both tracks/scales plus +four prior exact timeline probes pass,198enumeration. Check/strictClippy/fmt/diff +and independent source review pass.2source seals/17logs manifest1bbfbc8d…50e0cb. +Native repair/JPEG/force/runtime evidence remain separate and unqualified; production +force launch gate unchanged. Runtime ignored-test preparation is a subsequent slice. diff --git a/docs/plans/ui-refinement/reports/timeline-scaled-checkpoint.md b/docs/plans/ui-refinement/reports/timeline-scaled-checkpoint.md new file mode 100644 index 00000000..fda04ea2 --- /dev/null +++ b/docs/plans/ui-refinement/reports/timeline-scaled-checkpoint.md @@ -0,0 +1,38 @@ +# Narrow scaled timeline repair + +Native validator reported clipping of the track selector and time captions at +900×650physical/150%, with the setup280logical px and collapsed Scene64px panels. +This separate repair follows5eab3ac and changes only timeline layout plus its inert +regression. Runtime-test preparation remains separate. + +The selected track text is laid out against the full available row minus the +button decoration budget; ComboBox moves to a new row before its inner horizontal +layout begins when needed. Captions are prewrapped to the full row and placed as +whole widgets. Full labels, units, fonts, timeline-track identity, popup choices, +keyboard/input and playback-state transitions remain. No additional fixed height. + +A corrected exact UI regression reproduced real track text clipping before the +fix and passes after it for both tracks at100%/150%, cold and second paint. It uses +native scale via RawInput and literal presentation data, not App.update/history/ +Worker/physics/SQLite. An earlier scheduled-zoom setup failure is preserved but is +not the deciding production regression. Final five individually exact inert timeline +probes pass against198-name enumeration; locked desktop check1.15s, strict production +Clippy2.82s, formatter and diff checks pass. Independent source review and exact +source/log seals are committed. Manifest SHA256: +`1bbfbc8deeb2c40f149af2d9b073640ac74734bc75647ab44efca54ecd50e0cb`. + +Native repair remains UNVERIFIED. Existing validator owns the next native check; +no competing session was launched. At the exact repair revision, inspect both track +labels, selected time/cost captions, speed editing and popup at900×650/150% and +1250×820/100%, with Scene hidden and the ordinary setup sidebar present. Preserve +compact content-driven height while permitting necessary row growth. No simulation +is required for the already labelled workerless synthetic playback harness; it is +presentation data, not force/worker evidence. Any actual JPEG must carry exact source, +state and synthetic annotation at quality85; no new JPEG/Library ID is claimed here. + +Parent-reported8148fa2 synthetic playback/tracks/speed precision, Draft/Retained +restoration, scale and close checks passed. Production native sessions twice stalled +at initial frame: picking/outlines/handles/Escape/camera isolation/property sync remain +UNVERIFIED. Parent says owned processes stopped and older databases/evidence unchanged. +The native owner diagnoses those stalls separately. No numerical/model workload, +archive fixture, broad test filter, gate removal, cleanup or merge occurred here. diff --git a/docs/plans/ui-refinement/reports/timeline-scaled-source-review.md b/docs/plans/ui-refinement/reports/timeline-scaled-source-review.md new file mode 100644 index 00000000..b342ca65 --- /dev/null +++ b/docs/plans/ui-refinement/reports/timeline-scaled-source-review.md @@ -0,0 +1,44 @@ +# Independent narrow/scaled timeline source review + +## Admission and scope + +Source review in `/workspace/Rheon-ui-authoring`, HEAD `5eab3ac4b6d4005ae77b145b3d970b9692debd4c`. The parent relayed native observation at 900×650/150% that the track selector and time captions clip. This reviewer did not observe that native session. Initial-frame native stalls leave picking/handles unverified; this review performs no duplicate native work and makes no claim about those interactions. + +Final disposition: suitable for a separate bounded layout-repair checkpoint. The reproduced pure clipping failure is repaired in the focused Context probes, with no material source blocker. Actual native repair verification remains unverified. No runtime/recording qualification follows from this layout result. + +Admitted exact name: `timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip`, after list-only enumeration. Complete helper inspection found only local Context/style/AccessKit setup, App default, literal inert Request and PreviewFrame, direct `timeline_ui` measurement/real painting, shape/clip assertions and ordinary in-memory drop. No App update, history creation/capture/view, selected-frame/Inspector path, Worker/start/poll, numerical/model constructor or step, SQLite, output fixture or cleanup is reached. App has no worker/history; its Drop does not join work. The reviewer performed no build, test or native execution and edited only this report. + +The probe uses logical sizes 900/scale by 650/scale at 1 and 1.5 pixels per point. The initial source reserved only the left 280 px panel; review identified the missing collapsed Scene reservation. The owner added an inert right 64 px panel, without calling SceneUi, and a panel-bottom assertion against the actual logical height before exact execution. This produces the narrow production reservation: at 150%, 600 logical window width minus 280 and 64 leaves 256 for the timeline. Existing full-shape/clip assertions are retained; banner/screenshot/native rendering is outside this pure probe. + +An initial run using `Context::set_pixels_per_point` failed before establishing production clipping: the scheduled zoom can overwrite the first RawInput screen rectangle using the previous default 10000 px content rect. Pinned `ContextImpl::begin_pass` confirms that overwrite. The owner preserved that failed attempt, then changed the probe to set ROOT `ViewportInfo.native_pixels_per_point` on RawInput without scheduling zoom. This literal metadata assignment keeps the helper inert and is admitted under the same exact name. Its resulting cold logical geometry, rather than the artificial scheduled-zoom rectangle, must be used for deciding before/after evidence. + +## Deciding baseline and reviewed repair + +No production repair existed at initial admission. Read-only receipt of `/tmp/rheon-ui-timeline-scaled-baseline.log` establishes the corrected probe fails on actual Jacobi text clipping: glyph bounds x498.0…573.3 exceed the clip's x536 right edge in the 600×433.3 logical state. The run names only the admitted exact test, with zero passes, one failure and 197 filtered out. This is the deciding production baseline, separate from the preserved scheduled-zoom setup failure. + +The pinned egui 0.33.3 ComboBox builds an internal horizontal UI: a selected button cannot move itself to the parent wrapped row. This is the same mechanism already addressed by the density-unit row. The reviewed repair precomputes the complete selected galley against full-row width minus button/icon decorations, explicitly ends the row when the remainder cannot hold it, and sets the matching button width. `.width` alone would only be a minimum. The existing `timeline-track` salt, full discoverable/accessibility text, popup content and playback mutations are unchanged. Galley preparation and `end_row` introduce no widgets, focus state or alternate owner. + +The new `timeline_caption` prewraps each complete caption against full row width using the original Body font; the wrapped parent can then move that widget to the next row. This avoids deriving a galley from a tiny remainder. Time units, numerical/report context, copy and font size are preserved. Both invisible measurement and interactive painting call the same contents/helper; hidden sizing AccessKit subtree and single state commit remain. No global App replay or layout-specific playback state is added. WidgetText galley text remains available to label/ComboBox accessibility metadata. + +After a repair, re-read the exact production/test diff and preserve cold/warm 1×/1.5× containment evidence. Track popup, focused/invalid speed text and report rows remain separate states; prior wider-window results do not establish the new 256 px layout. Native verification is still required for actual scale/font/GPU behavior and pointer/keyboard usability. Runtime-force preparation remains a separate milestone with its production launch gate; this clipping work does not qualify numerical/archive behavior. + +## Evidence status + +The corrected deciding failure and production repair source were reviewed. The earlier `/tmp/rheon-ui-timeline-scaled-after1.log` records one exact repaired pass, with 197 filtered out, for the initial Jacobi-only probe. The owner then strengthened the same probe to loop both method identifiers at both scales and set the corresponding literal frame method; that source change was re-inspected and admitted without adding a runtime helper. The final result below covers that strengthened source. No screenshot or native interaction is claimed. + +Complete helper paths for these four existing timeline regressions were re-inspected and re-admitted for individual exact execution after enumeration: + +- `timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar` +- `timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time` +- `timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard` +- `timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states` + +They call only local Context, direct timeline helper, App default, literal Request/frame/report values, pointer/key input and shape/AccessKit checks. No History/Inspector/Worker/Viewport/native/model/solver/filesystem fixture or cleanup path is reached; App drop has no worker. Supplied report structs are synthetic presentation values, not workload results. + +Final reviewed production source SHA-256: `src/bin/rheon-desktop.rs` = `a704b5dbb9978449187346f4b6eb8f85a9769c96a3f8ddfe85a5dd98f2766282`; strengthened test source `src/bin/desktop/timeline_ui_tests.rs` = `606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b`. No source change occurred between the final gate receipt and these hash reads. + +Read-only final receipt: `/tmp/rheon-ui-timeline-scaled-list5.log` enumerates 198 tests and zero benchmarks. `final1.log` names the strengthened narrow test; `final2.log` through `final5.log` name the four regressions in the order above. Each records exactly one pass, zero failures, 197 filtered out. The narrow test checks both method labels at 1×/1.5×, cold/warm paints, logical panel-bottom/clip containment and height below 180 logical pixels with unchanged track/no worker/history. The wider regressions cover idle, playback transitions, bounded speed editing/invalid input and supplied report/track popup states. They do not establish every combination of narrow report stress, long configured names, focused editor and popup geometry. + +Locked desktop check log records success in 1.15 s and strict production Clippy in 2.82 s. The final fmt log is empty; the owner explicitly confirmed session 63287 completed with exit 0. Reviewer read-only `git diff --check` passed, also confirmed by the owner. No executable, test or build was run by the reviewer. + +Final transfer receipt: `evidence/timeline-scaled-20261021/manifest.json`, SHA-256 `1bbfbc8deeb2c40f149af2d9b073640ac74734bc75647ab44efca54ecd50e0cb`, was independently verified against both current source seals and all seventeen copied/raw log pairs by SHA-256 and size. The packet preserves the setup failure, true production baseline, intermediate single-track pass, final five passes, enumeration and static gates. It marks native verification and numerical execution false. No native repair result or unrelated runtime preparation is included in this acceptance. diff --git a/src/bin/desktop/timeline_ui_tests.rs b/src/bin/desktop/timeline_ui_tests.rs index e462dda1..629e8fb6 100644 --- a/src/bin/desktop/timeline_ui_tests.rs +++ b/src/bin/desktop/timeline_ui_tests.rs @@ -444,3 +444,55 @@ fn timeline_track_selection_and_report_rows_fit_cold_and_changed_states() { assert_visible(&output, rect); } } + +#[test] +fn timeline_narrow_scaled_track_and_time_captions_stay_inside_clip() { + for pixels_per_point in [1.0, 1.5] { + for track in 0..2 { + let ctx = context(); + let mut app = App::default(); + app.running_request = Some(inert_carrier_request()); + app.follow_live = false; + app.track = track; + let mut selected = presentation(); + selected.method = PressureImplementation::ALL[track]; + let mut panel = egui::Rect::NOTHING; + for _ in 0..2 { + let mut input = egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(900.0 / pixels_per_point, 650.0 / pixels_per_point), + )), + ..Default::default() + }; + input + .viewports + .get_mut(&egui::ViewportId::ROOT) + .unwrap() + .native_pixels_per_point = Some(pixels_per_point); + let output = ctx.run(input, |ctx| { + egui::SidePanel::left("timeline-scaled-sidebar") + .exact_width(280.0) + .resizable(false) + .show(ctx, |_| {}); + egui::SidePanel::right("timeline-scaled-scene-collapsed") + .exact_width(64.0) + .resizable(false) + .show(ctx, |_| {}); + panel = app.timeline_ui(ctx, 5, Some(&selected)); + }); + assert_visible(&output, panel); + assert!(panel.bottom() <= 650.0 / pixels_per_point + 0.5); + assert!(text_rect(&output, PressureImplementation::ALL[track].id()).is_positive()); + assert!(text_rect(&output, "Simulated 40.000 ms").is_positive()); + assert!(text_rect(&output, "Step calculation 2.000 ms").is_positive()); + assert!( + panel.height() < 180.0, + "narrow timeline should not consume half the logical window: {panel:?}" + ); + } + assert!(app.worker.is_none() && app.history.is_none()); + assert_eq!(app.track, track); + } + } +} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index e1e518d6..ed77824e 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -1196,6 +1196,17 @@ struct TimelineControls { playback_speed_text: String, playback_speed_invalid: bool, } +// Prewrap against the whole row, then let the wrapped layout place this caption +// as one widget. Layout against a small row remainder can clip split text. +fn timeline_caption(ui: &mut egui::Ui, text: String) { + let galley = egui::WidgetText::from(text).into_galley( + ui, + Some(egui::TextWrapMode::Wrap), + ui.max_rect().width(), + egui::TextStyle::Body, + ); + ui.label(galley); +} impl TimelineControls { fn contents( &mut self, @@ -1285,8 +1296,23 @@ impl TimelineControls { if let Some(request) = request && request.material.is_none() { + let decoration = 2.0 * ui.spacing().button_padding.x + + ui.spacing().icon_spacing + + ui.spacing().icon_width; + let text = egui::WidgetText::from(request.track_label(self.track)).into_galley( + ui, + Some(egui::TextWrapMode::Wrap), + (ui.max_rect().width() - decoration).max(1.0), + egui::TextStyle::Button, + ); + let width = text.size().x + decoration; + // ComboBox's inner horizontal UI cannot move itself to a new row. + if ui.available_size_before_wrap().x < width { + ui.end_row(); + } egui::ComboBox::from_id_salt("timeline-track") - .selected_text(request.track_label(self.track)) + .width(width) + .selected_text(text) .show_ui(ui, |ui| { for track in 0..request.methods.len() { if ui @@ -1319,39 +1345,51 @@ impl TimelineControls { } if let Some(frame) = &frame { ui.horizontal_wrapped(|ui| { - ui.label(format!( - "{} · Step {} / {}", - if frame.material_color.is_some() { - "Material fluid".to_owned() - } else { - track_caption.to_owned() - }, - frame.step, - count.saturating_sub(1) - )); + timeline_caption( + ui, + format!( + "{} · Step {} / {}", + if frame.material_color.is_some() { + "Material fluid".to_owned() + } else { + track_caption.to_owned() + }, + frame.step, + count.saturating_sub(1) + ), + ); ui.separator(); - ui.label(format!("Simulated {}", settings::duration(frame.time))); + timeline_caption(ui, format!("Simulated {}", settings::duration(frame.time))); ui.separator(); - ui.label(if frame.step == 0 { - "Initial state".into() - } else { - format!("Step calculation {}", settings::duration(frame.seconds)) - }); + timeline_caption( + ui, + if frame.step == 0 { + "Initial state".into() + } else { + format!("Step calculation {}", settings::duration(frame.seconds)) + }, + ); if let Some(r) = frame.particle_report { ui.separator(); - ui.label(format!( - "{} particles · {:.6} kg · {:.1} °C", - r.particles, - r.total_mass, - r.mean_temperature_kelvin - 273.15 - )); + timeline_caption( + ui, + format!( + "{} particles · {:.6} kg · {:.1} °C", + r.particles, + r.total_mass, + r.mean_temperature_kelvin - 273.15 + ), + ); } if let Some(report) = frame.report { ui.separator(); - ui.label(format!( - "{} pressure iterations · divergence {:.2e}", - report.pressure.iterations, report.actual_divergence_max - )); + timeline_caption( + ui, + format!( + "{} pressure iterations · divergence {:.2e}", + report.pressure.iterations, report.actual_divergence_max + ), + ); } }); } From 1b377742e2dd2df0cc90c99c3ad735bfd480caad Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:22:20 +0000 Subject: [PATCH 16/25] Avoid nested egui context locks in enabled viewport input --- .../.gitattributes | 1 + .../manifest.json | 79 ++++ .../rheon-ui-context-access-inventory.txt | 433 ++++++++++++++++++ .../rheon-ui-focus-lock-check.log | 2 + .../rheon-ui-focus-lock-clippy.log | 2 + .../rheon-ui-focus-lock-diff.log | 0 .../rheon-ui-focus-lock-exact1.json | 24 + .../rheon-ui-focus-lock-exact1.log | 6 + .../rheon-ui-focus-lock-exact2.json | 24 + .../rheon-ui-focus-lock-exact2.log | 6 + .../rheon-ui-focus-lock-exact3.json | 24 + .../rheon-ui-focus-lock-exact3.log | 6 + .../rheon-ui-focus-lock-fmt.log | 0 .../rheon-ui-focus-lock-list1.log | 204 +++++++++ .../rheon_ui_exact_timeout.py | 26 ++ docs/plans/ui-refinement/execution-ledger.md | 13 + .../reports/viewport-focus-lock-checkpoint.md | 44 ++ .../viewport-focus-lock-source-review.md | 49 ++ src/bin/desktop/viewport.rs | 70 ++- 19 files changed, 1012 insertions(+), 1 deletion(-) create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-context-access-inventory.txt create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-check.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-diff.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.json create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.json create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.json create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-list1.log create mode 100644 docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon_ui_exact_timeout.py create mode 100644 docs/plans/ui-refinement/reports/viewport-focus-lock-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/viewport-focus-lock-source-review.md diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/.gitattributes b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/manifest.json b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/manifest.json new file mode 100644 index 00000000..7eb30711 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/manifest.json @@ -0,0 +1,79 @@ +{ + "base": "d6e23b36ab618f8873d32c89c67a62e95f775c5b", + "source": { + "file": "src/bin/desktop/viewport.rs", + "sha256": "ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3" + }, + "enumeration": 199, + "exact_passes": 3, + "binary_sha256": "8935179a6738fd137fd2859bd865d8ddb0e1393586dbe366e9922f279902a008", + "native_verification": false, + "numerical_execution": false, + "files": [ + { + "file": "rheon-ui-context-access-inventory.txt", + "bytes": 33305, + "sha256": "289faffc17a1ffbb6c59133032293478638926bea5674901162c734baf2c1dc4" + }, + { + "file": "rheon-ui-focus-lock-check.log", + "bytes": 130, + "sha256": "a2bb21d6ee920a6663de33967befdde3ec5c255b3b4e962ac8f3e3312e204e93" + }, + { + "file": "rheon-ui-focus-lock-clippy.log", + "bytes": 130, + "sha256": "3856eab6f80feb9a95243e537de6199854ef0a153049ab2b61af84441cb51bc9" + }, + { + "file": "rheon-ui-focus-lock-diff.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-focus-lock-exact1.json", + "bytes": 740, + "sha256": "b9b475e5318a5e44557ce8d907a23bfeda1eb1f9fb21110ecc845b22b5b42942" + }, + { + "file": "rheon-ui-focus-lock-exact1.log", + "bytes": 225, + "sha256": "c217aaf18238a99acd151ed30e2e796699fb59caf025d9f76833304f5cc663d2" + }, + { + "file": "rheon-ui-focus-lock-exact2.json", + "bytes": 704, + "sha256": "e48bf4d77ecc881396459c9a36a43df8b18cd2ffbb4da6e3ca29fa6ac58665a6" + }, + { + "file": "rheon-ui-focus-lock-exact2.log", + "bytes": 190, + "sha256": "dac198eef4cdacc9a2dc17b27ae18d43a003343c316600f5dfccef6f2f274343" + }, + { + "file": "rheon-ui-focus-lock-exact3.json", + "bytes": 737, + "sha256": "dd361f1054c6bb144d4f4892ee1ff140cb4cf2f12361eb31791809f7658bb06a" + }, + { + "file": "rheon-ui-focus-lock-exact3.log", + "bytes": 222, + "sha256": "3c0596301ef93b916376aba0f466941f6aefb1cd6fe9fd663d70dffe5ef9fc8d" + }, + { + "file": "rheon-ui-focus-lock-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-focus-lock-list1.log", + "bytes": 20997, + "sha256": "637b2a6ab2de7a15ba2f5e29f5a5f7d4639acb65a7fc5c3c717351d1672eacf9" + }, + { + "file": "rheon_ui_exact_timeout.py", + "bytes": 1796, + "sha256": "fac641390156cd82a7554253ca27f300d48473b7c9ab8e986f6a26eef8ae27c1" + } + ] +} diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-context-access-inventory.txt b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-context-access-inventory.txt new file mode 100644 index 00000000..d0697afb --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-context-access-inventory.txt @@ -0,0 +1,433 @@ +src/bin/desktop/settings.rs-35- let response = ui.add(control.custom_formatter(move |value, decimals| { +src/bin/desktop/settings.rs-36- // Query when invoked, after DragValue registers keyboard focus. +src/bin/desktop/settings.rs:37: if ctx.memory(|memory| memory.has_focus(id)) { +src/bin/desktop/settings.rs-38- // Debug keeps round-trip precision and uses scientific notation for +src/bin/desktop/settings.rs-39- // tiny values, avoiding hundreds of leading zeroes in the editor. +src/bin/desktop/settings.rs-40- format!("{value:?}") +src/bin/desktop/settings.rs-41- } else { +src/bin/desktop/settings.rs-42- formatter.format(value, decimals) +src/bin/desktop/settings.rs-43- } +src/bin/desktop/settings.rs-44- })); +src/bin/desktop/settings.rs-45- if response.clicked() && response.gained_focus() { +src/bin/desktop/settings.rs-46- // The button selected its compact text; the edit buffer will be longer. +src/bin/desktop/settings.rs-47- // Align this initial selection only, preserving later caret placement. +src/bin/desktop/settings.rs-48- let mut state = egui::TextEdit::load_state(ui.ctx(), id).unwrap_or_default(); +src/bin/desktop/settings.rs-49- state +src/bin/desktop/settings.rs-50- .cursor +src/bin/desktop/settings.rs-51- .set_char_range(Some(egui::text::CCursorRange::two( +src/bin/desktop/settings.rs-52- egui::text::CCursor::new(0), +src/bin/desktop/settings.rs-53- egui::text::CCursor::new(format!("{value:?}").chars().count()), +src/bin/desktop/settings.rs-54- ))); +src/bin/desktop/settings.rs-55- state.store(ui.ctx(), id); +-- +src/bin/desktop/settings.rs-633- ); +src/bin/desktop/settings.rs-634- let _ = frame(&ctx, width, &mut controls, &mut side, false, vec![]); +src/bin/desktop/settings.rs:635: if let Some(id) = ctx.memory(|m| m.focused()) { +src/bin/desktop/settings.rs-636- visited.insert(id); +src/bin/desktop/settings.rs-637- } +src/bin/desktop/settings.rs-638- assert_eq!( +src/bin/desktop/settings.rs-639- material_bits(&controls), +src/bin/desktop/settings.rs-640- bits, +src/bin/desktop/settings.rs-641- "focus-only change for {}", +src/bin/desktop/settings.rs-642- preset.label() +src/bin/desktop/settings.rs-643- ); +src/bin/desktop/settings.rs-644- assert_eq!(format!("{:?}", controls.config), before); +src/bin/desktop/settings.rs-645- } +src/bin/desktop/settings.rs-646- } +src/bin/desktop/settings.rs-647- assert!(visited.len() >= 10, "traverse actual form controls"); +src/bin/desktop/settings.rs-648- let _ = frame( +src/bin/desktop/settings.rs-649- &ctx, +src/bin/desktop/settings.rs-650- width, +src/bin/desktop/settings.rs-651- &mut controls, +src/bin/desktop/settings.rs-652- &mut side, +src/bin/desktop/settings.rs-653- false, +-- +src/bin/desktop/settings.rs-978- output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); +src/bin/desktop/settings.rs-979- let focused = ctx +src/bin/desktop/settings.rs:980: .memory(|m| m.focused()) +src/bin/desktop/settings.rs-981- .and_then(|id| ctx.read_response(id)) +src/bin/desktop/settings.rs-982- .expect("help has keyboard focus"); +src/bin/desktop/settings.rs-983- assert!( +src/bin/desktop/settings.rs-984- focused +src/bin/desktop/settings.rs-985- .rect +src/bin/desktop/settings.rs-986- .contains(text_rect(&output, "Property help").unwrap().center()) +src/bin/desktop/settings.rs-987- ); +src/bin/desktop/settings.rs-988- let _ = frame( +src/bin/desktop/settings.rs-989- &ctx, +src/bin/desktop/settings.rs-990- 240.0, +src/bin/desktop/settings.rs-991- &mut controls, +src/bin/desktop/settings.rs-992- &mut side, +src/bin/desktop/settings.rs-993- false, +src/bin/desktop/settings.rs-994- key(egui::Key::Enter, Default::default()), +src/bin/desktop/settings.rs-995- ); +src/bin/desktop/settings.rs-996- output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); +src/bin/desktop/settings.rs-997- assert!(text_rect(&output, help).is_some()); +src/bin/desktop/settings.rs:998: let help_id = ctx.memory(|m| m.focused()).unwrap(); +src/bin/desktop/settings.rs-999- let _ = frame( +src/bin/desktop/settings.rs-1000- &ctx, +src/bin/desktop/settings.rs-1001- 240.0, +src/bin/desktop/settings.rs-1002- &mut controls, +src/bin/desktop/settings.rs-1003- &mut side, +src/bin/desktop/settings.rs-1004- false, +src/bin/desktop/settings.rs-1005- key(egui::Key::Space, Default::default()), +src/bin/desktop/settings.rs-1006- ); +src/bin/desktop/settings.rs-1007- output = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); +src/bin/desktop/settings.rs-1008- assert!(text_rect(&output, help).is_none()); +src/bin/desktop/settings.rs-1009- let _ = frame( +src/bin/desktop/settings.rs-1010- &ctx, +src/bin/desktop/settings.rs-1011- 240.0, +src/bin/desktop/settings.rs-1012- &mut controls, +src/bin/desktop/settings.rs-1013- &mut side, +src/bin/desktop/settings.rs-1014- false, +src/bin/desktop/settings.rs-1015- key(egui::Key::Tab, Default::default()), +src/bin/desktop/settings.rs-1016- ); +src/bin/desktop/settings.rs-1017- let _ = frame(&ctx, 240.0, &mut controls, &mut side, false, vec![]); +src/bin/desktop/settings.rs:1018: assert_ne!(ctx.memory(|m| m.focused()), Some(help_id)); +src/bin/desktop/settings.rs-1019- assert_eq!(format!("{:?}", controls.config), before); +src/bin/desktop/settings.rs-1020- } +src/bin/desktop/settings.rs-1021- +src/bin/desktop/settings.rs-1022- #[test] +src/bin/desktop/settings.rs-1023- fn ui_material_selection_updates_only_its_existing_owner() { +src/bin/desktop/settings.rs-1024- for width in [240.0, 280.0] { +src/bin/desktop/settings.rs-1025- for domain in [false, true] { +src/bin/desktop/settings.rs-1026- let ctx = context(); +src/bin/desktop/settings.rs-1027- let mut controls = MaterialControls::default(); +src/bin/desktop/settings.rs-1028- controls.config.fill = DomainFill::OtherFluid; +src/bin/desktop/settings.rs-1029- let mut expected = controls.config; +src/bin/desktop/settings.rs-1030- let mut side = 1.0; +src/bin/desktop/settings.rs-1031- let _ = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); +src/bin/desktop/settings.rs-1032- let output = frame(&ctx, width, &mut controls, &mut side, domain, vec![]); +src/bin/desktop/settings.rs-1033- let label = if domain { +src/bin/desktop/settings.rs-1034- "Air · 20 °C" +src/bin/desktop/settings.rs-1035- } else { +src/bin/desktop/settings.rs-1036- "Water · 20 °C" +-- +src/bin/desktop/configured_comparison_tests.rs-113- let mut view = ComparisonFieldView::default(); +src/bin/desktop/configured_comparison_tests.rs-114- view.load(selected(root, verified)).unwrap(); +src/bin/desktop/configured_comparison_tests.rs:115: let data = view.data().unwrap(); +src/bin/desktop/configured_comparison_tests.rs-116- assert_eq!(data.geometry.counts_xyz, [2; 3]); +src/bin/desktop/configured_comparison_tests.rs-117- assert_eq!(data.geometry.spacing_m, [0.25; 3]); +src/bin/desktop/configured_comparison_tests.rs-118- assert_eq!(data.sampling.association, SampleAssociation::CellCenter); +src/bin/desktop/configured_comparison_tests.rs-119- assert_eq!(data.sampling.dimensions, [2; 3]); +src/bin/desktop/configured_comparison_tests.rs-120- assert_eq!(data.reference.len(), 8); +src/bin/desktop/configured_comparison_tests.rs-121- assert_eq!(data.candidate.len(), 8); +src/bin/desktop/configured_comparison_tests.rs-122- for values in [&data.reference, &data.candidate] { +src/bin/desktop/configured_comparison_tests.rs-123- for (index, value) in values.iter().enumerate() { +src/bin/desktop/configured_comparison_tests.rs-124- // Independently known sealed hydrostatic solution: dp/dx=rho*a, +src/bin/desktop/configured_comparison_tests.rs-125- // rho=2, a=1/8, dx=1/4; cell-zero gauge yields x/16 Pa. +src/bin/desktop/configured_comparison_tests.rs-126- assert!((value - (index % 2) as f64 / 16.0).abs() <= EPS); +src/bin/desktop/configured_comparison_tests.rs-127- } +src/bin/desktop/configured_comparison_tests.rs-128- } +src/bin/desktop/configured_comparison_tests.rs-129- view.set_slice(SliceAxis::Z, 1).unwrap(); +src/bin/desktop/configured_comparison_tests.rs-130- view.set_probe([1, 1, 1]).unwrap(); +src/bin/desktop/configured_comparison_tests.rs-131- let probe = view.probe().unwrap(); +src/bin/desktop/configured_comparison_tests.rs-132- assert_eq!(probe.native_index, [1, 1, 1]); +src/bin/desktop/configured_comparison_tests.rs-133- assert_eq!(probe.physical_m, [0.375; 3]); +-- +src/bin/desktop/scene_view.rs-551-) { +src/bin/desktop/scene_view.rs-552- if response.clicked_by(egui::PointerButton::Primary) +src/bin/desktop/scene_view.rs:553: && ui.input(|i| i.modifiers.is_none()) +src/bin/desktop/scene_view.rs-554- && let Some(p) = response.interact_pointer_pos() +src/bin/desktop/scene_view.rs-555- { +src/bin/desktop/scene_view.rs-556- let projection = Projection::build(state, camera, response.rect, side, mesh); +src/bin/desktop/scene_view.rs-557- if let Ok(hit) = projection.pick(p, response.rect) { +src/bin/desktop/scene_view.rs-558- state.select(hit); +src/bin/desktop/scene_view.rs-559- if hit.is_some() { +src/bin/desktop/scene_view.rs-560- state.open = true; +src/bin/desktop/scene_view.rs-561- } +src/bin/desktop/scene_view.rs-562- } +src/bin/desktop/scene_view.rs-563- response.request_focus(); +src/bin/desktop/scene_view.rs-564- } +src/bin/desktop/scene_view.rs-565-} +src/bin/desktop/scene_view.rs-566-pub struct PaintArea { +src/bin/desktop/scene_view.rs-567- pub rect: Rect, +src/bin/desktop/scene_view.rs-568- pub warning_top: f32, +src/bin/desktop/scene_view.rs-569-} +src/bin/desktop/scene_view.rs-570-impl From for PaintArea { +src/bin/desktop/scene_view.rs-571- fn from(rect: Rect) -> Self { +-- +src/bin/desktop/native_presentation_harness.rs-127- if self.app.playing { +src/bin/desktop/native_presentation_harness.rs-128- self.app.play_time += +src/bin/desktop/native_presentation_harness.rs:129: self.app.playback_speed * f64::from(ctx.input(|i| i.stable_dt.min(0.1))); +src/bin/desktop/native_presentation_harness.rs-130- } +src/bin/desktop/native_presentation_harness.rs-131- let retained_view = self.app.prepare_scene_presentation(); +src/bin/desktop/native_presentation_harness.rs-132- let previous = self.app.scene.context; +src/bin/desktop/native_presentation_harness.rs-133- self.app.scene.show(ctx, true, false, None); +src/bin/desktop/native_presentation_harness.rs-134- if previous != self.app.scene.context { +src/bin/desktop/native_presentation_harness.rs-135- self.app.playing = false; +src/bin/desktop/native_presentation_harness.rs-136- } +src/bin/desktop/native_presentation_harness.rs-137- let retained = self.app.scene.context == scene_ui::SceneContext::Retained; +src/bin/desktop/native_presentation_harness.rs-138- let (count, frame) = if retained { +src/bin/desktop/native_presentation_harness.rs-139- retained_view.unwrap_or_else(|| self.app.selected_frame_bound_track()) +src/bin/desktop/native_presentation_harness.rs-140- } else { +src/bin/desktop/native_presentation_harness.rs-141- (0, None) +src/bin/desktop/native_presentation_harness.rs-142- }; +src/bin/desktop/native_presentation_harness.rs-143- let timeline = self.app.scene_timeline_ui(ctx, count, frame.as_deref()); +src/bin/desktop/native_presentation_harness.rs-144- self.app.viewport.domain_side = 1.0; +src/bin/desktop/native_presentation_harness.rs-145- self.app.viewport.authored_forces_pending = +src/bin/desktop/native_presentation_harness.rs-146- !self.app.scene.display_scene().objects.is_empty(); +src/bin/desktop/native_presentation_harness.rs-147- // Every frame resets to the fresh default Appearance owner. Disabled UI +-- +src/bin/desktop/ai_comparison/mod.rs-158- worker.join_finished(); +src/bin/desktop/ai_comparison/mod.rs-159- } +src/bin/desktop/ai_comparison/mod.rs:160: if ctx.input(|i| i.viewport().close_requested()) && self.running() { +src/bin/desktop/ai_comparison/mod.rs-161- self.closing = true; +src/bin/desktop/ai_comparison/mod.rs-162- if let Some(worker) = &self.worker { +src/bin/desktop/ai_comparison/mod.rs-163- worker.cancel(); +src/bin/desktop/ai_comparison/mod.rs-164- } +src/bin/desktop/ai_comparison/mod.rs-165- ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose); +src/bin/desktop/ai_comparison/mod.rs-166- } +src/bin/desktop/ai_comparison/mod.rs-167- if self.closing && !self.running() { +src/bin/desktop/ai_comparison/mod.rs-168- ctx.send_viewport_cmd(egui::ViewportCommand::Close); +src/bin/desktop/ai_comparison/mod.rs-169- } +src/bin/desktop/ai_comparison/mod.rs-170- let running = self.running(); +src/bin/desktop/ai_comparison/mod.rs-171- let mut stay = true; +src/bin/desktop/ai_comparison/mod.rs-172- egui::SidePanel::left("ai-comparison-controls").resizable(true).default_width(300.0).show(ctx,|ui|{ +src/bin/desktop/ai_comparison/mod.rs-173- ui.heading("Water pressure comparison");if ui.add_enabled(!running,egui::Button::new("Back to simulation")).clicked(){stay=false;} +src/bin/desktop/ai_comparison/mod.rs-174- ui.label("Filled sealed water · 1000 kg/m³ · 0.001 Pa·s · free-slip walls"); +src/bin/desktop/ai_comparison/mod.rs-175- ui.checkbox(&mut self.inspector.visible,"Inspect saved diagnostic attempts"); +src/bin/desktop/ai_comparison/mod.rs-176- ui.checkbox(&mut self.show_attempt,"Inspect latest live attempt"); +src/bin/desktop/ai_comparison/mod.rs-177- ui.add_enabled_ui(!running,|ui|{ +src/bin/desktop/ai_comparison/mod.rs-178- egui::ComboBox::from_label("Execution policy").selected_text(self.policy.label()).show_ui(ui,|ui|{ for policy in [engine::ExecutionPolicy::LegacyRawContinuation,engine::ExecutionPolicy::TeacherStage,engine::ExecutionPolicy::IndependentStopOnReject]{ui.selectable_value(&mut self.policy,policy,policy.label());}}); +-- +src/bin/desktop/ai_comparison/mod.rs-208- let mut latest_attempt = None; +src/bin/desktop/ai_comparison/mod.rs-209- let mut diagnostic_manifest = None; +src/bin/desktop/ai_comparison/mod.rs:210: let playback_delta = self.speed * f64::from(ctx.input(|i| i.stable_dt.min(0.1))); +src/bin/desktop/ai_comparison/mod.rs-211- if let Some(worker) = &self.worker +src/bin/desktop/ai_comparison/mod.rs-212- && let Ok(shared) = worker.shared.lock() +src/bin/desktop/ai_comparison/mod.rs-213- { +src/bin/desktop/ai_comparison/mod.rs-214- count = shared.frames.len(); +src/bin/desktop/ai_comparison/mod.rs-215- pressure_range = shared.pressure_range; +src/bin/desktop/ai_comparison/mod.rs-216- delta_range = shared.difference_range; +src/bin/desktop/ai_comparison/mod.rs-217- provider = shared.provider.clone(); +src/bin/desktop/ai_comparison/mod.rs-218- latest_attempt = shared.last_attempt.clone(); +src/bin/desktop/ai_comparison/mod.rs-219- diagnostic_manifest = shared.diagnostic_manifest.clone(); +src/bin/desktop/ai_comparison/mod.rs-220- self.status = format!("{} · {}", shared.state, shared.message); +src/bin/desktop/ai_comparison/mod.rs-221- if count > 0 { +src/bin/desktop/ai_comparison/mod.rs-222- if self.follow { +src/bin/desktop/ai_comparison/mod.rs-223- self.selected = count - 1; +src/bin/desktop/ai_comparison/mod.rs-224- } +src/bin/desktop/ai_comparison/mod.rs-225- if self.playing { +src/bin/desktop/ai_comparison/mod.rs-226- self.play_time += playback_delta; +src/bin/desktop/ai_comparison/mod.rs-227- self.selected = (self.play_time / scene::DT).floor() as usize; +src/bin/desktop/ai_comparison/mod.rs-228- if self.selected >= count - 1 { +-- +src/bin/desktop/ai_comparison/mod.rs-314- let scale=if self.fixed_range{self.manual_range}else{pressure_range};let error_scale=if self.fixed_range{self.manual_range}else{delta_range}; +src/bin/desktop/ai_comparison/mod.rs-315- let area=ui.available_rect_before_wrap(); +src/bin/desktop/ai_comparison/mod.rs:316: ctx.input(|input| { +src/bin/desktop/ai_comparison/mod.rs-317- if input.pointer.hover_pos().is_some_and(|p|area.contains(p)) { +src/bin/desktop/ai_comparison/mod.rs-318- if input.pointer.primary_down() && input.pointer.is_decidedly_dragging() { +src/bin/desktop/ai_comparison/mod.rs-319- let delta=input.pointer.delta(); +src/bin/desktop/ai_comparison/mod.rs-320- if input.modifiers.shift {self.camera.pan(delta,(area.height()-180.0).max(1.0));} +src/bin/desktop/ai_comparison/mod.rs-321- else if input.modifiers.ctrl {self.camera.zoom(-delta.y*0.01);} +src/bin/desktop/ai_comparison/mod.rs-322- else {self.camera.orbit(delta);} +src/bin/desktop/ai_comparison/mod.rs-323- } +src/bin/desktop/ai_comparison/mod.rs-324- self.camera.zoom(input.smooth_scroll_delta.y*0.003); +src/bin/desktop/ai_comparison/mod.rs-325- } +src/bin/desktop/ai_comparison/mod.rs-326- }); +src/bin/desktop/ai_comparison/mod.rs-327- ui.columns(2,|columns|{ +src/bin/desktop/ai_comparison/mod.rs-328- columns[0].allocate_ui_with_layout(egui::vec2(columns[0].available_width(),180.0),egui::Layout::top_down(egui::Align::Min),|ui|{ +src/bin/desktop/ai_comparison/mod.rs-329- ui.heading("Rheon reference pressure"); +src/bin/desktop/ai_comparison/mod.rs-330- if let Some(q)=&frame.reference_quality{ui.colored_label(Color32::LIGHT_GREEN,format!("Qualified · relative residual {:.2e}",q.relative_residual.unwrap_or(0.0)));}else{ui.label("Initial state");} +src/bin/desktop/ai_comparison/mod.rs-331- view::legend(ui,scale,"Reference pressure · Pa"); +src/bin/desktop/ai_comparison/mod.rs-332- }); +src/bin/desktop/ai_comparison/mod.rs-333- view::voxels(&mut columns[0],view::Display{scene:&self.active_scene,frame:&frame,camera:&self.camera,cut:self.cut,field:view::Field::Reference,scale},&mut self.probe); +src/bin/desktop/ai_comparison/mod.rs-334- columns[1].allocate_ui_with_layout(egui::vec2(columns[1].available_width(),180.0),egui::Layout::top_down(egui::Align::Min),|ui|{ +-- +src/bin/desktop/scene_handles.rs-367- editable: bool, +src/bin/desktop/scene_handles.rs-368- ) -> bool { +src/bin/desktop/scene_handles.rs:369: let (down, pressed, released, p, focused, gone) = ui.input(|i| { +src/bin/desktop/scene_handles.rs-370- ( +src/bin/desktop/scene_handles.rs-371- i.pointer.button_down(egui::PointerButton::Primary), +src/bin/desktop/scene_handles.rs-372- i.pointer.button_pressed(egui::PointerButton::Primary), +src/bin/desktop/scene_handles.rs-373- i.events.iter().any(|e| { +src/bin/desktop/scene_handles.rs-374- matches!( +src/bin/desktop/scene_handles.rs-375- e, +src/bin/desktop/scene_handles.rs-376- egui::Event::PointerButton { +src/bin/desktop/scene_handles.rs-377- button: egui::PointerButton::Primary, +src/bin/desktop/scene_handles.rs-378- pressed: false, +src/bin/desktop/scene_handles.rs-379- .. +src/bin/desktop/scene_handles.rs-380- } +src/bin/desktop/scene_handles.rs-381- ) +src/bin/desktop/scene_handles.rs-382- }), +src/bin/desktop/scene_handles.rs-383- i.pointer.latest_pos(), +src/bin/desktop/scene_handles.rs-384- i.focused, +src/bin/desktop/scene_handles.rs-385- i.events +src/bin/desktop/scene_handles.rs-386- .iter() +src/bin/desktop/scene_handles.rs-387- .any(|e| matches!(e, egui::Event::PointerGone)), +-- +src/bin/desktop/scene_handles.rs-405- || response.rect != drag.rect +src/bin/desktop/scene_handles.rs-406- || side.to_bits() != drag.side.to_bits(); +src/bin/desktop/scene_handles.rs:407: let escape = ui.input_mut(|i| i.consume_key(i.modifiers, egui::Key::Escape)); +src/bin/desktop/scene_handles.rs-408- if lost || escape { +src/bin/desktop/scene_handles.rs-409- self.cancel( +src/bin/desktop/scene_handles.rs-410- scene, +src/bin/desktop/scene_handles.rs-411- !released, +src/bin/desktop/scene_handles.rs-412- if escape { +src/bin/desktop/scene_handles.rs-413- "Edit cancelled" +src/bin/desktop/scene_handles.rs-414- } else { +src/bin/desktop/scene_handles.rs-415- "Edit cancelled · context changed" +src/bin/desktop/scene_handles.rs-416- }, +src/bin/desktop/scene_handles.rs-417- ); +src/bin/desktop/scene_handles.rs-418- return true; +src/bin/desktop/scene_handles.rs-419- } +src/bin/desktop/scene_handles.rs-420- let valid = scene.preview_edit(drag.candidate(p.unwrap())); +src/bin/desktop/scene_handles.rs-421- self.message = if valid { +src/bin/desktop/scene_handles.rs-422- None +src/bin/desktop/scene_handles.rs-423- } else { +src/bin/desktop/scene_handles.rs-424- Some("Invalid preview · release cancels") +src/bin/desktop/scene_handles.rs-425- }; +-- +src/bin/desktop/scene_handles.rs-439- || !focused +src/bin/desktop/scene_handles.rs-440- || !response.contains_pointer() +src/bin/desktop/scene_handles.rs:441: || !ui.input(|i| i.modifiers.is_none()) +src/bin/desktop/scene_handles.rs-442- { +src/bin/desktop/scene_handles.rs-443- return false; +src/bin/desktop/scene_handles.rs-444- } +src/bin/desktop/scene_handles.rs-445- if scene.display_scene().freeze().is_err() { +src/bin/desktop/scene_handles.rs-446- return false; +src/bin/desktop/scene_handles.rs-447- } +src/bin/desktop/scene_handles.rs-448- let Some(p) = p else { +src/bin/desktop/scene_handles.rs-449- return false; +src/bin/desktop/scene_handles.rs-450- }; +src/bin/desktop/scene_handles.rs-451- let Some(base) = scene.selected_force().cloned() else { +src/bin/desktop/scene_handles.rs-452- return false; +src/bin/desktop/scene_handles.rs-453- }; +src/bin/desktop/scene_handles.rs-454- let Some(index) = hit( +src/bin/desktop/scene_handles.rs-455- &handles(&base, self.tool, camera, response.rect, side), +src/bin/desktop/scene_handles.rs-456- self.tool, +src/bin/desktop/scene_handles.rs-457- p, +src/bin/desktop/scene_handles.rs-458- camera.orthographic, +src/bin/desktop/scene_handles.rs-459- ) else { +-- +src/bin/desktop/setup_ui_tests.rs-256- ); +src/bin/desktop/setup_ui_tests.rs-257- } +src/bin/desktop/setup_ui_tests.rs:258: assert!(ctx.memory(|memory| memory.has_focus(text_id))); +src/bin/desktop/setup_ui_tests.rs-259- let modifiers = egui::Modifiers { +src/bin/desktop/setup_ui_tests.rs-260- ctrl: true, +src/bin/desktop/setup_ui_tests.rs-261- command: true, +src/bin/desktop/setup_ui_tests.rs-262- ..Default::default() +src/bin/desktop/setup_ui_tests.rs-263- }; +src/bin/desktop/setup_ui_tests.rs-264- let _ = frame( +src/bin/desktop/setup_ui_tests.rs-265- &ctx, +src/bin/desktop/setup_ui_tests.rs-266- size, +src/bin/desktop/setup_ui_tests.rs-267- vec![egui::Event::Key { +src/bin/desktop/setup_ui_tests.rs-268- key: egui::Key::End, +src/bin/desktop/setup_ui_tests.rs-269- physical_key: None, +src/bin/desktop/setup_ui_tests.rs-270- pressed: true, +src/bin/desktop/setup_ui_tests.rs-271- repeat: false, +src/bin/desktop/setup_ui_tests.rs-272- modifiers, +src/bin/desktop/setup_ui_tests.rs-273- }], +src/bin/desktop/setup_ui_tests.rs-274- |ctx| { +src/bin/desktop/setup_ui_tests.rs-275- app.sidebar(ctx); +src/bin/desktop/setup_ui_tests.rs-276- }, +-- +src/bin/desktop/setup_ui_tests.rs-334- }); +src/bin/desktop/setup_ui_tests.rs-335- assert!( +src/bin/desktop/setup_ui_tests.rs:336: !ctx.memory(|memory| memory.has_focus(text_id)), +src/bin/desktop/setup_ui_tests.rs-337- "status must permit keyboard exit" +src/bin/desktop/setup_ui_tests.rs-338- ); +src/bin/desktop/setup_ui_tests.rs-339- } +src/bin/desktop/setup_ui_tests.rs-340- } +src/bin/desktop/setup_ui_tests.rs-341-} +src/bin/desktop/setup_ui_tests.rs-342- +src/bin/desktop/setup_ui_tests.rs-343-#[test] +src/bin/desktop/setup_ui_tests.rs-344-fn ui_setup_uninspected_adapter_does_not_emit_launch_intent() { +src/bin/desktop/setup_ui_tests.rs-345- let ctx = context(); +src/bin/desktop/setup_ui_tests.rs-346- let size = egui::vec2(900.0, 1800.0); +src/bin/desktop/setup_ui_tests.rs-347- let mut controls = configured_controls::ConfiguredControls::default(); +src/bin/desktop/setup_ui_tests.rs-348- let mut requested = false; +src/bin/desktop/setup_ui_tests.rs-349- let mut paint = |ctx: &egui::Context| { +src/bin/desktop/setup_ui_tests.rs-350- egui::CentralPanel::default().show(ctx, |ui| { +src/bin/desktop/setup_ui_tests.rs-351- controls.show(ui, true); +src/bin/desktop/setup_ui_tests.rs-352- requested |= controls.launch_button(ui, true); +src/bin/desktop/setup_ui_tests.rs-353- }); +src/bin/desktop/setup_ui_tests.rs-354- }; +-- +src/bin/desktop/setup_ui_tests.rs-559- } +src/bin/desktop/setup_ui_tests.rs-560- let path_id = ctx +src/bin/desktop/setup_ui_tests.rs:561: .memory(|memory| memory.focused()) +src/bin/desktop/setup_ui_tests.rs-562- .expect("path has pointer focus"); +src/bin/desktop/setup_ui_tests.rs-563- let modifiers = egui::Modifiers { +src/bin/desktop/setup_ui_tests.rs-564- ctrl: true, +src/bin/desktop/setup_ui_tests.rs-565- command: true, +src/bin/desktop/setup_ui_tests.rs-566- ..Default::default() +src/bin/desktop/setup_ui_tests.rs-567- }; +src/bin/desktop/setup_ui_tests.rs-568- for key in [egui::Key::End, egui::Key::A] { +src/bin/desktop/setup_ui_tests.rs-569- let _ = frame( +src/bin/desktop/setup_ui_tests.rs-570- &ctx, +src/bin/desktop/setup_ui_tests.rs-571- size, +src/bin/desktop/setup_ui_tests.rs-572- vec![egui::Event::Key { +src/bin/desktop/setup_ui_tests.rs-573- key, +src/bin/desktop/setup_ui_tests.rs-574- physical_key: None, +src/bin/desktop/setup_ui_tests.rs-575- pressed: true, +src/bin/desktop/setup_ui_tests.rs-576- repeat: false, +src/bin/desktop/setup_ui_tests.rs-577- modifiers, +src/bin/desktop/setup_ui_tests.rs-578- }], +src/bin/desktop/setup_ui_tests.rs-579- &mut paint, +-- +src/bin/desktop/viewport.rs-217- if self.mode != ViewMode::Appearance { +src/bin/desktop/viewport.rs-218- std::mem::swap(&mut self.camera, &mut self.engineering_camera); +src/bin/desktop/viewport.rs:219: self.input(ui, &response, rect, 1.8); +src/bin/desktop/viewport.rs-220- match self.mode { +src/bin/desktop/viewport.rs-221- ViewMode::Obstacle => { +src/bin/desktop/viewport.rs-222- if let Some(lab) = &mut self.engineering { +src/bin/desktop/viewport.rs-223- lab.paint(ui, rect, &response, &self.camera); +src/bin/desktop/viewport.rs-224- } +src/bin/desktop/viewport.rs-225- } +src/bin/desktop/viewport.rs-226- ViewMode::Particles => self.particles.paint( +src/bin/desktop/viewport.rs-227- ui, +src/bin/desktop/viewport.rs-228- rect, +src/bin/desktop/viewport.rs-229- &response, +src/bin/desktop/viewport.rs-230- &self.camera, +src/bin/desktop/viewport.rs-231- self.domain_side, +src/bin/desktop/viewport.rs-232- inspector, +src/bin/desktop/viewport.rs-233- ), +src/bin/desktop/viewport.rs-234- ViewMode::Transport => { +src/bin/desktop/viewport.rs-235- if let Some(Ok(view)) = &mut self.transport { +src/bin/desktop/viewport.rs-236- view.view.paint(ui, rect, &response, &self.camera); +src/bin/desktop/viewport.rs-237- } +-- +src/bin/desktop/viewport.rs-253- }); +src/bin/desktop/viewport.rs-254- if !consumed { +src/bin/desktop/viewport.rs:255: self.input(ui, &response, rect, 2.35); +src/bin/desktop/viewport.rs-256- } +src/bin/desktop/viewport.rs-257- if !consumed && let Some(scene) = scene.as_deref_mut() { +src/bin/desktop/viewport.rs-258- let mesh = self +src/bin/desktop/viewport.rs-259- .collision_mesh +src/bin/desktop/viewport.rs-260- .as_ref() +src/bin/desktop/viewport.rs-261- .map(|m| super::scene_view::MeshRef { +src/bin/desktop/viewport.rs-262- vertices: m.vertices(), +src/bin/desktop/viewport.rs-263- triangles: m.triangles(), +src/bin/desktop/viewport.rs-264- }); +src/bin/desktop/viewport.rs-265- super::scene_view::interact(ui, &response, &self.camera, self.domain_side, mesh, scene); +src/bin/desktop/viewport.rs-266- } +src/bin/desktop/viewport.rs-267- let changed = match (&self.frame, &frame) { +src/bin/desktop/viewport.rs-268- (Some(a), Some(b)) => !Arc::ptr_eq(a, b), +src/bin/desktop/viewport.rs-269- (None, None) => false, +src/bin/desktop/viewport.rs-270- _ => true, +src/bin/desktop/viewport.rs-271- }; +src/bin/desktop/viewport.rs-272- if changed || self.cached_threshold != self.threshold { +src/bin/desktop/viewport.rs-273- match frame +-- +src/bin/desktop/viewport.rs-570- return; +src/bin/desktop/viewport.rs-571- } +src/bin/desktop/viewport.rs:572: ui.input(|input| { +src/bin/desktop/viewport.rs-573- let middle = input.pointer.button_down(egui::PointerButton::Middle); +src/bin/desktop/viewport.rs-574- let primary = input.pointer.button_down(egui::PointerButton::Primary); +src/bin/desktop/viewport.rs-575- if (middle || primary) && response.dragged() { +src/bin/desktop/viewport.rs-576- let delta = input.pointer.delta(); +src/bin/desktop/viewport.rs-577- if input.modifiers.ctrl { +src/bin/desktop/viewport.rs-578- self.camera.zoom(-delta.y * 0.01); +src/bin/desktop/viewport.rs-579- } else if input.modifiers.shift { +src/bin/desktop/viewport.rs-580- self.camera.pan(delta, rect.height()); +src/bin/desktop/viewport.rs-581- } else { +src/bin/desktop/viewport.rs-582- self.camera.orbit(delta); +src/bin/desktop/viewport.rs-583- } +src/bin/desktop/viewport.rs-584- } +src/bin/desktop/viewport.rs-585- for event in &input.events { +src/bin/desktop/viewport.rs-586- match event { +src/bin/desktop/viewport.rs-587- egui::Event::MouseWheel { +src/bin/desktop/viewport.rs-588- unit, +src/bin/desktop/viewport.rs-589- delta, +src/bin/desktop/viewport.rs-590- modifiers, diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-check.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-check.log new file mode 100644 index 00000000..027f0754 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.22s diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-clippy.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-clippy.log new file mode 100644 index 00000000..5e775e24 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.81s diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-diff.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-diff.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.json b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.json new file mode 100644 index 00000000..70d9c4d0 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.json @@ -0,0 +1,24 @@ +{ + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock", + "--exact", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_seconds": 10, + "cpu_seconds": 5, + "address_space_bytes": 536870912, + "log_file_bytes": 1048576, + "term_grace_seconds": 1 + }, + "pid": 58359, + "exit_code": 0, + "timed_out": false, + "actions": [], + "elapsed_seconds": 0.017028679001668934, + "binary_sha256": "8935179a6738fd137fd2859bd865d8ddb0e1393586dbe366e9922f279902a008", + "captured_bytes": 225, + "log_sha256": "c217aaf18238a99acd151ed30e2e796699fb59caf025d9f76833304f5cc663d2" +} diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.log new file mode 100644 index 00000000..02188933 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact1.log @@ -0,0 +1,6 @@ + +running 1 test +test viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 198 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.json b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.json new file mode 100644 index 00000000..120dec04 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.json @@ -0,0 +1,24 @@ +{ + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt", + "--exact", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_seconds": 10, + "cpu_seconds": 5, + "address_space_bytes": 536870912, + "log_file_bytes": 1048576, + "term_grace_seconds": 1 + }, + "pid": 58397, + "exit_code": 0, + "timed_out": false, + "actions": [], + "elapsed_seconds": 0.01654094099649228, + "binary_sha256": "8935179a6738fd137fd2859bd865d8ddb0e1393586dbe366e9922f279902a008", + "captured_bytes": 190, + "log_sha256": "dac198eef4cdacc9a2dc17b27ae18d43a003343c316600f5dfccef6f2f274343" +} diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.log new file mode 100644 index 00000000..27171074 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact2.log @@ -0,0 +1,6 @@ + +running 1 test +test viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 198 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.json b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.json new file mode 100644 index 00000000..e8ef5e52 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.json @@ -0,0 +1,24 @@ +{ + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path", + "--exact", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_seconds": 10, + "cpu_seconds": 5, + "address_space_bytes": 536870912, + "log_file_bytes": 1048576, + "term_grace_seconds": 1 + }, + "pid": 58406, + "exit_code": 0, + "timed_out": false, + "actions": [], + "elapsed_seconds": 0.016865763998794137, + "binary_sha256": "8935179a6738fd137fd2859bd865d8ddb0e1393586dbe366e9922f279902a008", + "captured_bytes": 222, + "log_sha256": "3c0596301ef93b916376aba0f466941f6aefb1cd6fe9fd663d70dffe5ef9fc8d" +} diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.log new file mode 100644 index 00000000..6515f1d4 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-exact3.log @@ -0,0 +1,6 @@ + +running 1 test +test viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 198 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-fmt.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-list1.log b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-list1.log new file mode 100644 index 00000000..8f96b195 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon-ui-focus-lock-list1.log @@ -0,0 +1,204 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 9.51s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +199 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon_ui_exact_timeout.py b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon_ui_exact_timeout.py new file mode 100644 index 00000000..39c86fc2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/viewport-focus-lock-20261021/rheon_ui_exact_timeout.py @@ -0,0 +1,26 @@ +import hashlib, json, os, resource, signal, subprocess, sys, time +from pathlib import Path +binary, name, prefix = sys.argv[1:] +log=Path(prefix+'.log');receipt=Path(prefix+'.json') +assert not log.exists() and not receipt.exists() +limits={'wall_seconds':10,'cpu_seconds':5,'address_space_bytes':536870912,'log_file_bytes':1048576,'term_grace_seconds':1} +def bound(): + for key,value in [(resource.RLIMIT_CPU,5),(resource.RLIMIT_AS,536870912),(resource.RLIMIT_FSIZE,1048576),(resource.RLIMIT_CORE,0)]: + resource.setrlimit(key,(value,value)) +def group_signal(pid, sig): + try: os.killpg(pid,sig) + except ProcessLookupError: pass +start=time.monotonic();timed_out=False;actions=[] +with log.open('xb') as output: + child=subprocess.Popen([binary,name,'--exact','--test-threads=1','--nocapture'],stdout=output,stderr=subprocess.STDOUT,start_new_session=True,preexec_fn=bound) + try: code=child.wait(timeout=10) + except subprocess.TimeoutExpired: + timed_out=True;actions.append('TERM owned process group');group_signal(child.pid,signal.SIGTERM) + try: code=child.wait(timeout=1) + except subprocess.TimeoutExpired: + actions.append('KILL owned process group');group_signal(child.pid,signal.SIGKILL);code=child.wait(timeout=1) +data=log.read_bytes() +result={'command':[binary,name,'--exact','--test-threads=1','--nocapture'],'limits':limits,'pid':child.pid,'exit_code':code,'timed_out':timed_out,'actions':actions,'elapsed_seconds':time.monotonic()-start,'binary_sha256':hashlib.sha256(Path(binary).read_bytes()).hexdigest(),'captured_bytes':len(data),'log_sha256':hashlib.sha256(data).hexdigest()} +receipt.write_text(json.dumps(result,indent=2)+'\n') +print(json.dumps(result)) +assert not timed_out and code==0 and b'1 passed; 0 failed; 0 ignored;' in data diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index fe953b6d..992a2c76 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -500,3 +500,16 @@ four prior exact timeline probes pass,198enumeration. Check/strictClippy/fmt/dif and independent source review pass.2source seals/17logs manifest1bbfbc8d…50e0cb. Native repair/JPEG/force/runtime evidence remain separate and unqualified; production force launch gate unchanged. Runtime ignored-test preparation is a subsequent slice. + +## Priority enabled viewport focus-lock repair —2026-10-21 + +After separated6e23b3 timeline repair, snapshot focus/keyboard ownership before +Ui::input. Pinned egui source confirms425a986 guard re-entered exclusive Context +lock on plain enabled hover; native stall attribution remains qualified without +live stack/exact repair verification. Eleven read-input +one input_mut callbacks +independently inspected, no additional nested acquisition found. New enabled Scene +hover/Home/editorF exact probe and two prior direct viewport input probes passed +once each under10s/5CPU/512MiB/1MiB wrapper; no timeout/signal, no surviving childPID. +199-name enumeration, check/strictClippy/fmt/diff pass. No numerical/model/SQLite or +native execution. Preserve failed native evidence. This enabled path is distinct +from disabled synthetic presentation; native picking/handles remain UNVERIFIED. diff --git a/docs/plans/ui-refinement/reports/viewport-focus-lock-checkpoint.md b/docs/plans/ui-refinement/reports/viewport-focus-lock-checkpoint.md new file mode 100644 index 00000000..eeb46f50 --- /dev/null +++ b/docs/plans/ui-refinement/reports/viewport-focus-lock-checkpoint.md @@ -0,0 +1,44 @@ +# Enabled viewport context-lock repair + +The focus guard introduced in425a986 called Response::has_focus and Context's +wants_keyboard_input inside Ui::input. In pinned egui0.33.3, input holds an exclusive +Context write guard; both focus queries acquire that same context. This is a +source-proven nonreentrant lock re-entry even on ordinary hover without Home/F. +The parent native diagnosis found sleeping/low-CPU stalls, but had no live stack; +attribution of those recorded stalls remains qualified until exact native repair +verification. Keep all failed native captures/logs intact. + +Production repair: snapshot the keyboard-ownership boolean before Ui::input and +use that value inside. response.dragged inside the callback reads flags only. +Other camera math, event processing, input/focus behavior and gesture ownership +are unchanged. Independent inspection of eleven production read-input callbacks +and one input_mut in desktop/main found no additional nested context acquisition. +This is a bounded source inspection, not a general static-analysis certificate. + +The new exact test paints the enabled production Appearance/Scene path with empty +canonical scene, no worker/frame/Inspector/model/solver; it exercises plain hover, +Home framing and F while TextEdit owns focus. It does not use the disabled synthetic +canvas. Existing exact primary drag and native-touchpad input probes also passed, +each individually, once, after list-only199-name enumeration and helper review. + +Each binary invocation ran under the independently reviewed external wrapper: +10s wall,5s CPU,512MiB address space,1MiB log/file,core0; own child process group, +TERM then1s grace/KILL. All3 exited0, no timeout/signal action, captured225/190/222 +bytes. Child PIDs were absent afterward. Wrapper/rawlogs/JSON/source/binary seals +are preserved. The wrapper is scoped to these no-spawn, no-fixture paths, not a +new security sandbox or live-cancellation supervisor. No pre-fix local timeout +reproduction was run; source inspection and the parent's native diagnosis motivated +the repair, and these logs establish post-fix enabled-path behavior only. + +Locked desktop check1.22s, strict production Clippy2.81s, fmt/diff passed. No native +app, numerical/model workload, archive fixture or cleanup occurred. No JPEG or +Library identity is claimed. Production authored-force launches remain gated. + +Native handoff: build the exact repair tip on PR146 including separate parent +scaled-timeline repaird6e23b3. First confirm idle enabled canvas accepts hover, +resize and close. Then inspect actual picking/outlines/handles/Escape/camera isolation +and property synchronization in the existing native owner's session. The earlier +8148fa2 disabled synthetic playback passes do not cover this enabled input path; +all previously unverified native gesture claims remain unverified until that owner +reports exact-source outcomes. No solver run is needed for idle force authoring. +Runtime/SQLite test preparation is a separate subsequent slice. diff --git a/docs/plans/ui-refinement/reports/viewport-focus-lock-source-review.md b/docs/plans/ui-refinement/reports/viewport-focus-lock-source-review.md new file mode 100644 index 00000000..e059b349 --- /dev/null +++ b/docs/plans/ui-refinement/reports/viewport-focus-lock-source-review.md @@ -0,0 +1,49 @@ +# Independent viewport focus-lock source review + +## Disposition and scope + +Suitable for a separate minimal fix checkpoint over `d6e23b36ab618f8873d32c89c67a62e95f775c5b` in `/workspace/Rheon-ui-authoring`, with three bounded exact passes and the static receipts below. The parent relayed native initial-frame stalls. The source establishes a nested context-lock defect, but this review has no native stack trace or qualified attribution proving it caused those native stalls. Actual native picking/handles remain unverified. + +Only `src/bin/desktop/viewport.rs` changes production behavior: keyboard ownership is sampled before entering the input callback. The reviewer read source/dependency/inventory files and wrote only this report; no test, build, numerical/model workload, native window or executable was run. Runtime preparation remains separate and is not admitted by this review. + +## Lock mechanism and repair + +Pinned egui 0.33.3 `Context::input` invokes `Context::write`, holding the exclusive context lock for its callback. Its documentation explicitly warns against context use inside that callback. `Response::has_focus` calls its context's input and memory accessors; `Context::wants_keyboard_input` accesses memory. Calling either inside `Viewport::input`'s input closure therefore attempts another acquisition while the exclusive guard is held. This is a source-proven lock reentry defect on enabled hovered/dragged input, even without a Home/F key event: the keyboard-ownership condition is evaluated first. In debug builds the dependency's lock wrapper may eventually panic after its timeout; this is not normal progress. + +The repair computes `response.has_focus() || !ui.ctx().wants_keyboard_input()` before `ui.input`, then uses that scalar inside. Evaluation order/shortcut policy is preserved: Home/F frames the viewport only when it owns keyboard or no widget owns keyboard. Drag, wheel, pinch, modifier, numeric view shortcuts, camera limits and gesture ownership are unchanged. `response.dragged()` remains in the callback, but pinned Response implementation reads a local flag and does not acquire the context; `dragged_by` would differ and is not used there. OrbitCamera orbit/pan/zoom operate only on values. + +## Similar callback inventory + +Read `/tmp/rheon-ui-context-access-inventory.txt` (SHA-256 `289faffc17a1ffbb6c59133032293478638926bea5674901162c734baf2c1dc4`) and inspected complete current bodies rather than relying on grep context. The inventory was captured before this fix and includes test-only focus queries; it is discovery evidence, not a complete semantic proof by itself. + +The eleven production `input` read callbacks inspected are: main timeline speed keys, main close request, main playback delta; AI-comparison close request, playback delta and camera input; viewport camera input; scene-handle pointer/focus snapshot and modifier query; scene selection modifier query; synthetic-wrapper playback delta. The additional scene-handle `input_mut` callback only consumes Escape on the supplied InputState. All remaining closures read supplied input or update plain camera values; no further nested UI/Context/Response context accessor was found. Response focus checks in handle code occur after the tuple snapshot closure ends; speed focus checks precede its key closure. The AI camera helper calls only camera value methods inside input. This is a scoped source inspection, not a whole-dependency deadlock certification. + +Settings' custom DragValue formatter queries context memory outside an input callback. The pinned DragValue calls that formatter directly after its separate input/memory reads have returned. Its existing precision/focus behavior is not changed here. + +## Exact path admission + +Only these three fully qualified exact names are admitted after the 199-name list-only enumeration: + +- `viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock` +- `viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt` +- `viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path` + +Complete helper paths were inspected. Each constructs only a local egui Context and default Viewport, which has default Appearance mode and absent engineering/transport/mesh/frame owners. The new test additionally uses an empty literal SceneUi, a text editor and enabled production `show_scene` composition. Pointer positions target the canvas, not lab tabs; no mode change is requested. Scene handles have no selected force and no drag; scene projection validates empty metadata. Appearance paints an empty display mesh/grid/captions with no emitter, workers, frame, model or Inspector. The other two tests use the test-only `show` delegate with Scene None, then the same enabled Appearance input path. No App, Worker, history, solver/model constructor, recorder/reader, SQLite, filesystem fixture or cleanup is reached. All ownership drops in memory. + +The new test covers plain enabled hover, Home framing and F with a focused TextEdit preserving camera distance; it checks lab owners remain absent and the presentation-only guard is false. The earlier tests cover primary orbit/Shift-pan/Ctrl-zoom and point-scroll/Shift-pan/pinch. They are local Context/camera interaction probes, not native GPU/input or force-handle acceptance. No pre-fix local execution/reproduction is proposed; source analysis and final post-fix enabled-path coverage must be described accordingly. + +## Scoped external timeout wrapper + +Reviewed `/tmp/rheon_ui_exact_timeout.py`, SHA-256 `fac641390156cd82a7554253ca27f300d48473b7c9ab8e986f6a26eef8ae27c1`, before execution admission. It launches one exact child in a new session/process group; uses a 10 s wait, TERM, 1 s grace, then KILL and a final 1 s wait; imposes child CPU 5 s, address space 512 MiB, file size 1 MiB and core size zero; captures stdout/stderr to a fresh raw log and retains a JSON receipt. ProcessLookupError during signaling is tolerated as an exit race. Success requires exit zero, no timeout and an actual one-pass result. Invoke ordinary Python (not `-O`, which would disable its assertions) with one of the three inspected names and fresh output prefixes only. + +This wrapper is admitted for these trusted no-spawn/no-fixture helpers, not as a general security/containment supervisor. Resource limits are per process; the group boundary relies on the admitted helper paths, and this does not sandbox unrelated binaries or provision cgroups. Final wait/receipt I/O failure can leave an unsuccessful or uncertain receipt and must not be treated as success. The wrapper does not authorize numerical tests, general descendant workloads, existing artifact deletion or retry after uncertain execution. + +## Final source identity and evidence + +Reviewed viewport source SHA-256 `ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3`; wrapper SHA and inventory identity remain as above. No production edits followed the accepted checks. No material source blocker remains for this small repair and the three bounded exact probes. + +Read-only receipt of `/tmp/rheon-ui-focus-lock-list1.log`: 199 tests, zero benchmarks. The three exact log/JSON pairs each name their corresponding admitted probe, record one pass with 198 filtered out, exit zero, no timeout and no TERM/KILL action. Captured logs are 225, 190 and 222 bytes; wrapper elapsed times are approximately 0.017 s each. All log SHA/size/result fields were independently checked against their JSON receipts. The current compiled test binary independently hashes to the receipts' `8935179a6738fd137fd2859bd865d8ddb0e1393586dbe366e9922f279902a008`. Readback confirms the three owned child PIDs are absent. This establishes post-fix enabled Context-path completion, Home/editor focus behavior and the existing camera regressions; no pre-fix execution is claimed. + +Locked desktop check passed in 1.22 s and strict production Clippy in 2.81 s. The owner confirmed fmt/diff session 31169 completed with exit zero; those logs are empty. Final copied packet `evidence/viewport-focus-lock-20261021/manifest.json`, SHA-256 `568ab94d6fd093753b6f213a26b7850f7cdced18db133942cbc4f5c3364747a1`, was independently verified against current source and all thirteen packet/raw file hash/size pairs, including logs, receipts, wrapper and inventory. It marks native verification and numerical execution false. The reviewer ran no build/test/executable. + +Native stall recovery and real picking/handles require the existing native validator; local completion must not be described as native attribution or acceptance. The three pure probes do not exercise a live run, recording/SQLite, model/runtime, numerical solver or worker cancellation, and provide no admission for the separate prepared runtime tests. diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index 0a697065..ada3afee 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -569,6 +569,9 @@ impl Viewport { if !(response.hovered() || response.dragged()) { return; } + // Response focus and Context keyboard ownership acquire context locks. + // Snapshot them before input holds its context guard. + let keyboard_owned = response.has_focus() || !ui.ctx().wants_keyboard_input(); ui.input(|input| { let middle = input.pointer.button_down(egui::PointerButton::Middle); let primary = input.pointer.button_down(egui::PointerButton::Primary); @@ -611,7 +614,7 @@ impl Viewport { _ => {} } } - if (response.has_focus() || !ui.ctx().wants_keyboard_input()) + if keyboard_owned && (input.key_pressed(egui::Key::Home) || input.key_pressed(egui::Key::F)) { self.camera = OrbitCamera { @@ -1015,6 +1018,71 @@ mod tests { ); } } + /// Execute by exact name under a hard external timeout: the pre-fix nested + /// context lock blocks even on plain hover, before any key is pressed. + #[test] + fn enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock() { + let ctx = egui::Context::default(); + let mut viewport = Viewport::default(); + let mut scene = + super::super::scene_ui::SceneUi::geometry_fixture(rheon::ForceScene::default(), false); + let mut editor = String::from("Keyboard owner"); + let mut draw = |viewport: &mut Viewport, key: Option, focus_editor: bool| { + let mut events = vec![egui::Event::PointerMoved(egui::pos2(400.0, 300.0))]; + if let Some(key) = key { + events.push(egui::Event::Key { + key, + physical_key: None, + pressed: true, + repeat: false, + modifiers: Default::default(), + }); + } + let _ = ctx.run( + egui::RawInput { + screen_rect: Some(Rect::from_min_size(Pos2::ZERO, egui::vec2(800.0, 600.0))), + events, + ..Default::default() + }, + |ctx| { + egui::SidePanel::left("focus-owner") + .exact_width(150.0) + .show(ctx, |ui| { + let response = ui.text_edit_singleline(&mut editor); + if focus_editor { + response.request_focus(); + } + }); + egui::CentralPanel::default().show(ctx, |ui| { + assert!(ui.is_enabled()); + viewport.show_scene( + ui, + ViewInputs { + frame: None, + emitter: None, + workers: &[], + grid_edge: 2, + inspector: None, + scene: Some(&mut scene), + editable_scene: true, + }, + ); + }); + }, + ); + }; + draw(&mut viewport, None, false); + draw(&mut viewport, None, false); // enabled, hovered, no shortcut needed + viewport.camera.distance = 4.0; + draw(&mut viewport, Some(egui::Key::Home), false); + assert_eq!(viewport.camera.distance, 2.35); + draw(&mut viewport, None, true); + viewport.camera.distance = 4.0; + draw(&mut viewport, Some(egui::Key::F), true); + assert_eq!(viewport.camera.distance, 4.0, "text editor owns keyboard"); + assert!(viewport.engineering.is_none() && viewport.transport.is_none()); + assert!(!viewport.presentation_only); + } #[test] fn native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path() { let ctx = egui::Context::default(); From 3b3fccea3657d711897130643a478adb48b5edd0 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:31:40 +0000 Subject: [PATCH 17/25] Prepare ignored bounded force worker and archive integration probes --- .../.gitattributes | 1 + .../manifest.json | 73 + .../rheon-ui-force-worker-check.log | 2 + .../rheon-ui-force-worker-clippy.log | 2 + ...heon-ui-force-worker-enumeration-check.log | 4 + .../rheon-ui-force-worker-fmt.log | 0 .../rheon-ui-force-worker-ignored-list.log | 8 + .../rheon-ui-force-worker-list1.log | 19 + .../rheon-ui-force-worker-list2.log | 206 +++ .../rheon-ui-force-worker-plan-check.log | 1 + .../rheon-ui-force-worker-toolchain.log | 1 + .../source-files.json | 1392 +++++++++++++++++ docs/plans/ui-refinement/execution-ledger.md | 23 + docs/plans/ui-refinement/plan.md | 12 +- .../runtime-force-validation-contract.md | 291 ++-- .../runtime-force-worker-test-checkpoint.md | 75 + .../runtime-force-worker-test-plan-review.md | 224 +++ ...runtime-force-worker-test-source-review.md | 61 + .../force_runtime_integration_tests.rs | 370 +++++ src/bin/rheon-desktop.rs | 3 + 20 files changed, 2646 insertions(+), 122 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/.gitattributes create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-check.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-enumeration-check.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-ignored-list.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list1.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-plan-check.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-toolchain.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json create mode 100644 docs/plans/ui-refinement/reports/runtime-force-worker-test-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-worker-test-plan-review.md create mode 100644 docs/plans/ui-refinement/reports/runtime-force-worker-test-source-review.md create mode 100644 src/bin/desktop/force_runtime_integration_tests.rs diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/.gitattributes b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/.gitattributes new file mode 100644 index 00000000..b4b94832 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/.gitattributes @@ -0,0 +1 @@ +*.log binary diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/manifest.json b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/manifest.json new file mode 100644 index 00000000..07bf2ff5 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/manifest.json @@ -0,0 +1,73 @@ +{ + "base": "1b377742e2dd2df0cc90c99c3ad735bfd480caad", + "enumerated_total": 201, + "ignored_total": 4, + "new_ignored_names": [ + "force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2", + "force_runtime_integration_tests::material_force_dispatch_records_immutable_scene" + ], + "new_test_bodies_executed": false, + "production_force_gate": "retained", + "source_files": 278, + "source_manifest_sha256": "603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc", + "features": "desktop (includes parallel/work-observation)", + "toolchain": "rustc 1.92.0 (ded5c06cf 2025-12-08)", + "binary": { + "path": "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "bytes": 480456920, + "sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "coverage": "compile/list-only; no test bodies run" + }, + "logs": [ + { + "file": "rheon-ui-force-worker-check.log", + "bytes": 130, + "sha256": "4a0f564ca3f82fb658024102001a34a6aeb304f4a1e1191c0c7b6261bf93639a" + }, + { + "file": "rheon-ui-force-worker-clippy.log", + "bytes": 130, + "sha256": "9ba41c9e20602bc796c4cd448795b50ce21a662ac08043d9432867337a607780" + }, + { + "file": "rheon-ui-force-worker-enumeration-check.log", + "bytes": 411, + "sha256": "0b6ea05b3f167d44b00d80a84dfeb993701fb0cc073525e0303f3c10513b0778" + }, + { + "file": "rheon-ui-force-worker-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-force-worker-ignored-list.log", + "bytes": 548, + "sha256": "08e2a6dd88187753f71f4cf09acf8822b62ba4544bdf9ff093451d2185673585" + }, + { + "file": "rheon-ui-force-worker-list1.log", + "bytes": 1025, + "sha256": "8e4b6ec273ba481703441b8890566f4d0418efc8ff58408536503a667cbad2de" + }, + { + "file": "rheon-ui-force-worker-list2.log", + "bytes": 21172, + "sha256": "e4b30714a7b61e095bbc1980ec2ae516d034ee04309029a2e42bbe483a5f9c9f" + }, + { + "file": "rheon-ui-force-worker-plan-check.log", + "bytes": 73, + "sha256": "e9c82b35d330356eedfb44a275872ea55dc89b934202099f25236e5cd9baa8cd" + }, + { + "file": "rheon-ui-force-worker-toolchain.log", + "bytes": 36, + "sha256": "ecda74ca828b18debf1624cb36209a5b844fb1e1c1905ffb3d51b56cc275f96f" + } + ], + "document_seals": { + "reports/runtime-force-validation-contract.md": "1b96cb00db82e92978282c213c4abea54ee06e382c5f994edb6185f99fd5ad71", + "reports/runtime-force-worker-test-plan-review.md": "1855fff92b3c1d89cd4727b84b6a901d1d3f9227d3766fff0cf7ef14137552ce", + "reports/runtime-force-worker-test-source-review.md": "bb8dbb93c36ddc4910841550c84f03ddc87b76153b9adcd17fb696fc32653629" + } +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-check.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-check.log new file mode 100644 index 00000000..706f5777 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.29s diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-clippy.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-clippy.log new file mode 100644 index 00000000..a185aac2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.78s diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-enumeration-check.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-enumeration-check.log new file mode 100644 index 00000000..21e03d72 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-enumeration-check.log @@ -0,0 +1,4 @@ +Confirmed ignored: force_runtime_integration_tests::material_force_dispatch_records_immutable_scene +Confirmed ignored: force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2 +Four ignored tests total: these two plus two legacy cases. No test body executed. +Earlier validation expected two total ignored cases and failed its count assertion; this corrects that enumeration-only mistake. diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-fmt.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-ignored-list.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-ignored-list.log new file mode 100644 index 00000000..240fc340 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-ignored-list.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.22s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +setup_ui_tests::bounded_genuine_cancel_contract: test + +4 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list1.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list1.log new file mode 100644 index 00000000..164dcb9e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list1.log @@ -0,0 +1,19 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error[E0603]: enum `SnapshotStage` is private + --> src/bin/desktop/force_runtime_integration_tests.rs:4:76 + | + 4 | use rheon::recording::{RecordedCarrier, RecordedMaterial, RecordingReader, SnapshotStage}; + | ^^^^^^^^^^^^^ private enum + | +note: the enum `SnapshotStage` is defined here + --> /workspace/Rheon-ui-header-verify/src/recording.rs:14:44 + | +14 | use crate::{GridGeometry, SessionIdentity, SnapshotStage}; + | ^^^^^^^^^^^^^ +help: import `SnapshotStage` directly + | + 4 | use rheon::recording::{RecordedCarrier, RecordedMaterial, RecordingReader, rheon::session::SnapshotStage}; + | ++++++++++++++++ + +For more information about this error, try `rustc --explain E0603`. +error: could not compile `rheon` (bin "rheon-desktop" test) due to 1 previous error diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log new file mode 100644 index 00000000..9e120664 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log @@ -0,0 +1,206 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 13.86s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +201 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-plan-check.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-plan-check.log new file mode 100644 index 00000000..47466b55 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-plan-check.log @@ -0,0 +1 @@ +Pinned structural plan contract passed; no standards-engine certificate. diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-toolchain.log b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-toolchain.log new file mode 100644 index 00000000..aa7f3992 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-toolchain.log @@ -0,0 +1 @@ +rustc 1.92.0 (ded5c06cf 2025-12-08) diff --git a/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json new file mode 100644 index 00000000..fd5f8f3c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json @@ -0,0 +1,1392 @@ +[ + { + "file": "Cargo.lock", + "bytes": 104189, + "sha256": "3b5836a7be9f1290c698cadaff010e11b93e33b4af839c8549391c23e7d8ec0c" + }, + { + "file": "Cargo.toml", + "bytes": 2332, + "sha256": "f925dae7a0d74e6024e6e0d64962a275ae4f28fedec5b016341671021b8239ae" + }, + { + "file": "browser/solver/src/bin/flow-reference.rs", + "bytes": 4609, + "sha256": "4be0fde70f7babe84e2b736d0515b3b3ff44df8388e2dc9a0c82c0d99fc131fa" + }, + { + "file": "browser/solver/src/bin/reference.rs", + "bytes": 204, + "sha256": "4fbcdf17e81882e22895830149cc3db6a607c1e25276ab5025305f5e527c2e65" + }, + { + "file": "browser/solver/src/flow.rs", + "bytes": 13616, + "sha256": "dce9c79650a69a423ad5bec33cc679274c03471c25639f6be9a353dc5804cdb7" + }, + { + "file": "browser/solver/src/lib.rs", + "bytes": 9887, + "sha256": "144dd1ac312c7d83e91f62dbebe1ed83b3f96b2f9a7d50110b7c0577b6bac2b7" + }, + { + "file": "browser/solver/tests/contract.rs", + "bytes": 5975, + "sha256": "e598f0cccb79dfb94b890579f1e652cd2c9e44c5581ce45969a61c4405cd97e9" + }, + { + "file": "browser/solver/tests/flow_contract.rs", + "bytes": 7931, + "sha256": "2b67707339d47360c7fff71de6ede7cba45185759d08499f6b43e5d96062c70c" + }, + { + "file": "docs/plans/emitter-controls/reports/default-baseline-probe.rs", + "bytes": 2567, + "sha256": "89720995df134c8fcaf4ee15cb8a3fd8954d7eb019397ca32e55eadb0751393c" + }, + { + "file": "evidence/coupled-terminal-main/baseline-probe-source.rs", + "bytes": 2304, + "sha256": "8c47def3bd74d2c12f3035f046fb25a9e3ea01884e54f6de956fe8e768c3356b" + }, + { + "file": "evidence/extruded-third-velocity/native-trials/first-export-source.rs", + "bytes": 7984, + "sha256": "7b493980de835d69f1c367137d14f37636564f90f9fdb80df1a02ad403bff08b" + }, + { + "file": "evidence/hit-reconstruction/exact-red-witness.rs", + "bytes": 656, + "sha256": "8ce5176f109fdfa7eb12eb4d4fafcc77eb77b32d71240bb1bb0ad9a8fb9fa878" + }, + { + "file": "evidence/pressure-divergence-restart/probe-pr3.rs", + "bytes": 1282, + "sha256": "0f7781786a40fcece2b7b01af4e5ad386d9e8e457870e923533d33c5b9d4c1d0" + }, + { + "file": "evidence/pressure-divergence-restart/probe.rs", + "bytes": 1015, + "sha256": "5be8fe73716fed669cb09e46339f0aa3c0f0b188e206d1551adbcf1a66c267e4" + }, + { + "file": "evidence/translated-viscous-flow/trials/baseline-source/examples__translated_viscous.rs", + "bytes": 4733, + "sha256": "cd21ba55572510da15203f2c0b0cfc268a3289248759222b967f20ad51b82c3c" + }, + { + "file": "evidence/translated-viscous-flow/trials/baseline-source/src__lib.rs", + "bytes": 4187, + "sha256": "8f194e2204315c81d907f0ae454b2bd91358ea950f9bd4685ae66225b6222134" + }, + { + "file": "evidence/translated-viscous-flow/trials/baseline-source/src__translated_viscous.rs", + "bytes": 25274, + "sha256": "4702f4e42ab31c3122fa7b1fd332370a43d33d4fd6fd98ec3d52abb97e28fed7" + }, + { + "file": "evidence/translated-viscous-flow/trials/baseline-source/tests__translated_viscous_contract.rs", + "bytes": 7077, + "sha256": "c122407674f191512ba27668d2af5c9e35a0cceb1ece911a3efdfe0c30bcc06d" + }, + { + "file": "examples/ai_pressure.rs", + "bytes": 6799, + "sha256": "1231f6cf933132841fc189f034850797ef25e16dd9850da9106a1e58e48710b4" + }, + { + "file": "examples/ai_pressure_trajectory.rs", + "bytes": 15279, + "sha256": "c4d37c4ab0aa8d10855b687290c2d2ee6df7e5b7390da530dc9da6c8fa767661" + }, + { + "file": "examples/aligned_stokes.rs", + "bytes": 15894, + "sha256": "52c7852f9688c62ba33ac6f0de2fe0df669214d7eda9bd1566332ea211f129f8" + }, + { + "file": "examples/aligned_strain.rs", + "bytes": 8135, + "sha256": "11910f4536bbb0057bd7462864f4d2af07d37dcd02103f41d1b93674dcca4547" + }, + { + "file": "examples/box_flux.rs", + "bytes": 2519, + "sha256": "0f4f642e746644eaaa7fa3742bd3f6e89ef6e8e952a9e3c1bf0830824eada212" + }, + { + "file": "examples/box_flux_step.rs", + "bytes": 3960, + "sha256": "8aa8c3e1168671605a75e96b7cdb17ed01abde9a6cf57300a8664a2fd6698d0e" + }, + { + "file": "examples/cloud_frame_lab.rs", + "bytes": 3779, + "sha256": "083dc1a9464de2f60122a09b1736632059259a3a63a7fe2dec5b416e32fe451e" + }, + { + "file": "examples/collision_mesh.rs", + "bytes": 5822, + "sha256": "a1036f9f9cef2aadc3cf3a7c7f2b8d291860c64c2636e518968ee777599a64ef" + }, + { + "file": "examples/column_interface.rs", + "bytes": 13526, + "sha256": "ad55a5c74a7a857c79b06b4ac44025cce1c11782533499bfca3028a6b1b2af2e" + }, + { + "file": "examples/column_mac.rs", + "bytes": 13275, + "sha256": "b9e6d8e4e3d34fe12b04af40ab5dd9f98fe4ffe16aaad669cf00ab74dd6a81cf" + }, + { + "file": "examples/column_momentum.rs", + "bytes": 8016, + "sha256": "803835cd8c22e58e9087d635cb07e9ac02aaebedcfaff70405ded71e866046f8" + }, + { + "file": "examples/column_no_slip.rs", + "bytes": 10191, + "sha256": "88316856dd6d08453807e67b36035ab848c556435cb55d61222b7bb73abd1ac8" + }, + { + "file": "examples/column_poiseuille.rs", + "bytes": 14555, + "sha256": "d44c8a14e2418c4b04ae9e030117fc08389ab8c701df62313941c816573c26ec" + }, + { + "file": "examples/column_shear.rs", + "bytes": 10063, + "sha256": "2fc9fc6797580365b4c86be0c2de5cb63d22ea062165f85e4a1c1322b121967c" + }, + { + "file": "examples/column_wall_shear.rs", + "bytes": 8889, + "sha256": "49c22a359ac4d512e2b564704e062483173a5dcc31af7d5a2bc409b2da18d72d" + }, + { + "file": "examples/compressible_reference.rs", + "bytes": 12610, + "sha256": "6eaad82dae42b7c6cd38fe45b2a8a1c140d9cbbd1d4b097fec607b6d7307900e" + }, + { + "file": "examples/coupled_discrete.rs", + "bytes": 5649, + "sha256": "01f09d383328da2af3a094d819fcfff75272ca6c812d8bd66516fbd855caa3b0" + }, + { + "file": "examples/coupled_discrete_identity.rs", + "bytes": 5761, + "sha256": "17d07a49f71cdb198706d7569bb222fbcfdf62d25342b9c07c35fe6edc80e7ed" + }, + { + "file": "examples/coupled_layout_probe.rs", + "bytes": 755, + "sha256": "d33838f8f533c9cdb2bb6e9fb6938fcfcac0b24c948da77f521b26f632cf02da" + }, + { + "file": "examples/cpu_particle_lab.rs", + "bytes": 3262, + "sha256": "3e6f4eb91403c285805115733fd423e2ed071b6f3024a49daee35db527539e1c" + }, + { + "file": "examples/cpu_pressure_lab.rs", + "bytes": 6125, + "sha256": "60e0782c88c3e61fbe2723c81b9f9d4863f7c35db02739732570d076af318c18" + }, + { + "file": "examples/dense3d_sequence.rs", + "bytes": 4751, + "sha256": "47a4adc4446a8e084c1fd580f3b08c36a3552d573336f9e7d7b5689c07b50d94" + }, + { + "file": "examples/emitter_controls.rs", + "bytes": 1751, + "sha256": "c411cc0826169dd117c09166c7a5ab30594c873373dc95bf00937ade7f28f0ea" + }, + { + "file": "examples/extruded_third.rs", + "bytes": 8147, + "sha256": "bac7303bda410e3856b6fcd7a9511ab5ebb7ebc8355b72978b66fa8f79cd3ebc" + }, + { + "file": "examples/fitted_height.rs", + "bytes": 4838, + "sha256": "563fd60664a956e2e7049765b70c7752c04c4191d4788e8917042128efce899a" + }, + { + "file": "examples/fitted_pressure_path.rs", + "bytes": 9988, + "sha256": "44144d68df4ac6ccbfcd1432f07216a3cbd45f36e77e2f949dce99e4bcc6fd2a" + }, + { + "file": "examples/fixed_bottom_ale.rs", + "bytes": 6656, + "sha256": "8faec9c41e38e793fad09512632891415959b228d01e0cd8dceb46c84c649089" + }, + { + "file": "examples/fluid_physics_audit.rs", + "bytes": 22308, + "sha256": "a2ce3e4f0d3f8b2bca2ddb0f04f33d462a5ff17912c9264efdc60ef1f630f830" + }, + { + "file": "examples/forced_smoke.rs", + "bytes": 2607, + "sha256": "945462fcc489b492e6df08bf82f396b4f9b6fce4ee96efbc69969b2309c0bcf9" + }, + { + "file": "examples/free_surface.rs", + "bytes": 9047, + "sha256": "24f5d252fe6fb2350c94a773c92482a65a5ebd1f6f8672d4e67fd85b770cded8" + }, + { + "file": "examples/game_loop_session.rs", + "bytes": 1358, + "sha256": "394155c0f3801b070a0e291bd4708d69cf1135e118e7347997d77fa517829cc8" + }, + { + "file": "examples/headless_session.rs", + "bytes": 682, + "sha256": "07b884a8cd399311551db2970f7dd994d8e61ba23206fe79dad37445c601ea19" + }, + { + "file": "examples/liquid_step.rs", + "bytes": 8170, + "sha256": "ea38303027b9c88e5ba8d5f3d7e78f1de7032ce7349795fde6eade97c0c1d777" + }, + { + "file": "examples/liquid_volume.rs", + "bytes": 3560, + "sha256": "5875c7ee5f2fc803a26779e6a824c0844c4ddceefec18648a08edea0fde2168e" + }, + { + "file": "examples/material_bounce_lab.rs", + "bytes": 2585, + "sha256": "8ef3f4dc9db690dea5021ede9ba35c98a349cf2845b7b47f9beca1f83d9ed984" + }, + { + "file": "examples/material_session.rs", + "bytes": 2306, + "sha256": "0f56645f825f137774962bb0e5a77d5d0fcb2163424cad9f49c4efbb7614dc74" + }, + { + "file": "examples/obstacle_flow.rs", + "bytes": 9512, + "sha256": "c153dc8e97f5bb6c7e662a63ee6c76a255356b3cda7b13ea3847d80fbbbb0254" + }, + { + "file": "examples/outward_probe.rs", + "bytes": 2698, + "sha256": "20e6a0c25aec4018ca206d9aab33b57ee58e43e9eb7c22d20da74e9d77d3b1dd" + }, + { + "file": "examples/plate_coupling.rs", + "bytes": 5222, + "sha256": "7ad16deed68aadaea7ca5b03ad30716c75e56d092897dfa6b11394d074cede8c" + }, + { + "file": "examples/pressure_session.rs", + "bytes": 2329, + "sha256": "b81d7358679ec44a019f9ba7cb4912b1a605e6bacbaab4107b3e15b8e04999ac" + }, + { + "file": "examples/production_provider_session.rs", + "bytes": 3690, + "sha256": "74a6b847bd02523f284e51368336cf5f483f9dae19f4550d896d35416d7f8005" + }, + { + "file": "examples/recorded_carrier.rs", + "bytes": 4112, + "sha256": "43b0e7702d8e83341f9d0fa55adac936f578020acab64f4ebf1f4cdfd6b38f60" + }, + { + "file": "examples/recorded_configured_provider.rs", + "bytes": 8497, + "sha256": "d00e9504f395e3e46d016e157eb68bc2b95124a69c1fb8cf88f2b10da58c4ffa" + }, + { + "file": "examples/recorded_material.rs", + "bytes": 4297, + "sha256": "88996ea3a841d089096cbe3826626636a4000d952db4f0e0394bb26f2edfa7ba" + }, + { + "file": "examples/recording_storage_probe.rs", + "bytes": 6644, + "sha256": "3cd82787af8f36da99906f07b696e74fab48fd9ce0aeaeed76f20744d9197a53" + }, + { + "file": "examples/scheduling_lab.rs", + "bytes": 5790, + "sha256": "170db497caa9d8a6101c4966c5b59ebff9f152116a016fdda2b877739106ff6f" + }, + { + "file": "examples/session_support/mod.rs", + "bytes": 2266, + "sha256": "d74419edf6a45fb79ab444eb4977652ed68ffa047bb0f4ecb97866452b287db1" + }, + { + "file": "examples/static_obstacle.rs", + "bytes": 6869, + "sha256": "d6a6b832d9016335310211df215f19621ea01fd6aaded21bf02583d1c0187a66" + }, + { + "file": "examples/submarine_stokes.rs", + "bytes": 25752, + "sha256": "229287012e30d454ccfc6c160db6d2cdf783da36c43c27275c76b54e2b7c897b" + }, + { + "file": "examples/surface_queries.rs", + "bytes": 2526, + "sha256": "863cf617e25af715d8a1dc873a51067ec43b7d191d5cd94f2717c5712eef2be7" + }, + { + "file": "examples/tracer_barrier.rs", + "bytes": 2831, + "sha256": "48501413c3f16d2ffe3c38e2340e5b4d30f3a22454aea1af486f7462bd3cc811" + }, + { + "file": "examples/translated_viscous.rs", + "bytes": 5002, + "sha256": "599188c4f022da4690ac42fa1ffc1f216bb1775dad5f252daa7c42e1ceece62f" + }, + { + "file": "examples/translating_surface.rs", + "bytes": 1687, + "sha256": "b02d9021321929f4f70bc0fe8566a77a0c27680c45bdc26d969be560105b3462" + }, + { + "file": "examples/viscosity.rs", + "bytes": 11106, + "sha256": "2e947a838c36a30a51e0640216c8cf956e4cfcd7d70978552a22161d5da6752d" + }, + { + "file": "examples/vtk_export_fixture.rs", + "bytes": 7317, + "sha256": "32e5ee4e48321e93623f958792657223f535b0034fa5abb456cd6e7fc12ce6b8" + }, + { + "file": "experiments/aligned_stokes.rs", + "bytes": 25321, + "sha256": "d5b08df55c45c62fe801749746ade40026f53a1c14bce4792587fe0a9ff022a4" + }, + { + "file": "experiments/outward.rs", + "bytes": 8058, + "sha256": "226f82a681d1a8c0b0fc89e65de7ec40dcc085883ec7fbc5990f19ae36af292a" + }, + { + "file": "experiments/plate_coupling.rs", + "bytes": 15343, + "sha256": "1c8e16ebdebbbcf15e7b53ff09965f556dae47a6d06e7f5c060bb13959df7de2" + }, + { + "file": "experiments/submarine_stokes.rs", + "bytes": 69352, + "sha256": "ef534615daf2e2f304be064536cf08f54fb42d2ee21ed220d5ed1db524889efa" + }, + { + "file": "src/advection.rs", + "bytes": 8961, + "sha256": "0163a872c314b2b3bb7f732348aafdbfa7b162dceea74807b7e7e3eb60ab0849" + }, + { + "file": "src/aligned_strain.rs", + "bytes": 22750, + "sha256": "1541bc444d1b098c5a59a221b7068538b013362db46ed66e37eb4931ad425e4f" + }, + { + "file": "src/bin/desktop/accepted_session.rs", + "bytes": 986, + "sha256": "7a42a42bbe8d044f703c2c52d488bf8a4e8e3b3fb1448851efcfb04599d5bf31" + }, + { + "file": "src/bin/desktop/ai_comparison/accepted_provider.rs", + "bytes": 11005, + "sha256": "922c98958bec45dbd4dee199e0bbdbdd6e3fe4ac52d7a70472ce8132461416fc" + }, + { + "file": "src/bin/desktop/ai_comparison/accepted_provider_contract_tests.rs", + "bytes": 27732, + "sha256": "643636776b009337fb80dfdf3d5dbee975e06ed7f7a7cf3f534c07301b61c729" + }, + { + "file": "src/bin/desktop/ai_comparison/accepted_provider_tests.rs", + "bytes": 23364, + "sha256": "11ae34ba76dc93a87f15c8ce9f745403b2d22d0194987d945cf2edc53c624237" + }, + { + "file": "src/bin/desktop/ai_comparison/accepted_recording_contract_tests.rs", + "bytes": 36460, + "sha256": "6cfddefe84a64c89f126598b817d22afe6d1b5731e1d533ea92cecbd1870c942" + }, + { + "file": "src/bin/desktop/ai_comparison/adapter.rs", + "bytes": 26707, + "sha256": "7038fb09ef2bd33a17495414c4c0986a6fb63ba949fe440e9d19927baca712b2" + }, + { + "file": "src/bin/desktop/ai_comparison/configured_track.rs", + "bytes": 12926, + "sha256": "3959018b52697ef072884864c01de4682d1b4cd351c273426d914d02f0641fa1" + }, + { + "file": "src/bin/desktop/ai_comparison/configured_track_tests.rs", + "bytes": 29183, + "sha256": "a0dcc1c09d188ad14fc42e78535cd51bcb9bacae0522f4f95262340e4bd86b7c" + }, + { + "file": "src/bin/desktop/ai_comparison/cpu_contract_tests.rs", + "bytes": 14465, + "sha256": "6232225760a7646d2b6759f3df7bd2fee34bed00c6a3a1058e68a96fced4311e" + }, + { + "file": "src/bin/desktop/ai_comparison/diagnostic_runs.rs", + "bytes": 21835, + "sha256": "80559d9d69d8164c24b8dd45f71c51b972fc8af9349c9e56b3e36b14b827b41c" + }, + { + "file": "src/bin/desktop/ai_comparison/diagnostic_view.rs", + "bytes": 36493, + "sha256": "1e6b8011f945ba05ce04c8e4c7f88de23bd53fc99c776311fd30d3021a1c7fe2" + }, + { + "file": "src/bin/desktop/ai_comparison/diagnostic_worker.rs", + "bytes": 14292, + "sha256": "a75e78beff3c85dec528c54c15a29c6ce6db7c918279bda2af7b5af91104991b" + }, + { + "file": "src/bin/desktop/ai_comparison/diagnostic_worker_tests.rs", + "bytes": 27017, + "sha256": "fd39822957fed70fac97bf5025cdb57028817c414175320b901119dac4fa50a8" + }, + { + "file": "src/bin/desktop/ai_comparison/engine.rs", + "bytes": 32246, + "sha256": "610ba29f66eb91083f9bbd26b823b14e4bde4887eb057e929c089d6cd4592e55" + }, + { + "file": "src/bin/desktop/ai_comparison/engine_typed_tests.rs", + "bytes": 10907, + "sha256": "584b5fd3dc9307f6fbf758474cd3f02e6ffcefb00e90ca3f068b838c15c786ad" + }, + { + "file": "src/bin/desktop/ai_comparison/mod.rs", + "bytes": 28365, + "sha256": "83f8864382f0c0c8c454b54dc0e9c389654a325849c6802b7205a2b031040076" + }, + { + "file": "src/bin/desktop/ai_comparison/model_process.rs", + "bytes": 40530, + "sha256": "bbbe43744cad1791038e07895967f744b86bb77fa9642d0fc80e4b00bee2204d" + }, + { + "file": "src/bin/desktop/ai_comparison/prediction_contract_tests.rs", + "bytes": 12980, + "sha256": "088668d3b29f3650c2145f09199d7ff8cbf38e1de78b1c4d73c882e8fc1610b3" + }, + { + "file": "src/bin/desktop/ai_comparison/record.rs", + "bytes": 7107, + "sha256": "2464e347414d3c75e7568dda6a5fd102896c5f194cf3ba8b3bb34961566fce0d" + }, + { + "file": "src/bin/desktop/ai_comparison/scene.rs", + "bytes": 5274, + "sha256": "c8817ed933ff31c4e9cdd7c0eb2e4c35f50a6868568496e2c67444e11ad80321" + }, + { + "file": "src/bin/desktop/ai_comparison/view.rs", + "bytes": 9171, + "sha256": "568f7b852a21714405e37d55ba00a56b3f4b07e58037ed26f8a67cfd4fbd268d" + }, + { + "file": "src/bin/desktop/ai_comparison/worker.rs", + "bytes": 18705, + "sha256": "6fb6c5b49cf57a473f9911d89965e8812c5d9915ecc1d3b654ca2f7ecfa8cc6b" + }, + { + "file": "src/bin/desktop/archive_view.rs", + "bytes": 9299, + "sha256": "9f96f1d6b11b46e7fd523825e19a8fc0e0ec862669acd129f5e39e91f83c89ff" + }, + { + "file": "src/bin/desktop/build_identity.rs", + "bytes": 987, + "sha256": "a94fea1f039a49f8d0eb79b019b3fbd0f2c9da1762ba854d10cba1e5fd87a25b" + }, + { + "file": "src/bin/desktop/camera.rs", + "bytes": 3055, + "sha256": "b6bcc84a45bd5979de357c965c84426c25e81161ee1f06022e57a93a46643f7d" + }, + { + "file": "src/bin/desktop/carrier_execution.rs", + "bytes": 8697, + "sha256": "c4455879373fc71eaaf398fdaa230c4d0b115dc7ff0a9da6356afeb24cba23b2" + }, + { + "file": "src/bin/desktop/collision_ui.rs", + "bytes": 7895, + "sha256": "78d7be8e49798e2420ebac5c3fb756e1b195e43ef113ae03ce1b2d3362009204" + }, + { + "file": "src/bin/desktop/comparison.rs", + "bytes": 46042, + "sha256": "27ad4b88db848430a68f5f9fae796fa10eab1df26af9dbae73386e55c9b5c74f" + }, + { + "file": "src/bin/desktop/comparison_archive.rs", + "bytes": 19518, + "sha256": "813cc86187ef7313deacbe9d9cd15340f37ecdf62f5234a45f033909415459b9" + }, + { + "file": "src/bin/desktop/comparison_field_view.rs", + "bytes": 32998, + "sha256": "ec8ea0799522d5a7fadac47b06b42f26717f935bbb9a1677fc5052f096c1cc82" + }, + { + "file": "src/bin/desktop/configured_comparison_tests.rs", + "bytes": 31956, + "sha256": "e6716e8c707f30cc20304b16e3201f8fb1fd838650bd08a43a95ab4d68cbbf46" + }, + { + "file": "src/bin/desktop/configured_controls.rs", + "bytes": 37252, + "sha256": "5463e18b3b5a139c448dd327b23318605664e90aea4690187981177e7f68e60b" + }, + { + "file": "src/bin/desktop/configured_producer_exit_tests.rs", + "bytes": 17651, + "sha256": "7560ff5395acbd50ce111b1e50ae5e8aca1b1bab5356ec195929eec0994f24bb" + }, + { + "file": "src/bin/desktop/configured_worker_tests.rs", + "bytes": 23533, + "sha256": "dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016" + }, + { + "file": "src/bin/desktop/emitter_controls.rs", + "bytes": 5728, + "sha256": "d5eaf6358b037d1f11177abbbd83bfa8aa18c263b195c3911b8f183b3334fba9" + }, + { + "file": "src/bin/desktop/engineering.rs", + "bytes": 32960, + "sha256": "9fc4eaafd1118abda50b70d1cc71c843fc1ca71d420d2cd47f16a20fcc9e8e9d" + }, + { + "file": "src/bin/desktop/export_ui.rs", + "bytes": 2539, + "sha256": "1702f437b17dd42d2bf1311ca14d0301a53712392abc6591ab8962b48cba511d" + }, + { + "file": "src/bin/desktop/force_runtime_integration_tests.rs", + "bytes": 14735, + "sha256": "da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584" + }, + { + "file": "src/bin/desktop/force_runtime_ui_tests.rs", + "bytes": 15963, + "sha256": "0f8b88db0553cf079c0f78a2e41766fcb6160f6dda1a3d117f1ce93fe17b91f5" + }, + { + "file": "src/bin/desktop/history.rs", + "bytes": 21523, + "sha256": "e4be154bbbf40a36f370b68ec9e15fea663fb589d476a43eb298c0e8c0f0d821" + }, + { + "file": "src/bin/desktop/identity.rs", + "bytes": 429, + "sha256": "df451a986476b9df252a9a4dbc1e33218bf5ab3cb268b83fa78b47dfffdae184" + }, + { + "file": "src/bin/desktop/inspector.rs", + "bytes": 46546, + "sha256": "aad6760e94582d6512c32387609c3c2874e92728c93c3c7d2089cbf3bc6ee62c" + }, + { + "file": "src/bin/desktop/native_presentation_harness.rs", + "bytes": 11577, + "sha256": "38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6" + }, + { + "file": "src/bin/desktop/particle_view.rs", + "bytes": 8253, + "sha256": "75208acd210d4558a34127b98cf77f2c898a560f991f6bed3bee3a4eb160b9a2" + }, + { + "file": "src/bin/desktop/run_set_view.rs", + "bytes": 5164, + "sha256": "ab2c841612f39b16f5bd1a363593eae5388bf4a81ec495dfb89e43bd7436580b" + }, + { + "file": "src/bin/desktop/run_sets.rs", + "bytes": 35607, + "sha256": "b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0" + }, + { + "file": "src/bin/desktop/run_sets/configured.rs", + "bytes": 30187, + "sha256": "45fa8cf6976da7f8ab3eff27fd69ba5511c98c4be541739eefcdf762826abac9" + }, + { + "file": "src/bin/desktop/run_sets/configured/archive.rs", + "bytes": 17423, + "sha256": "eb1d8e77019eb57afd9e5fd5f57af55f8ff320527ec8efc935945bd7ff477806" + }, + { + "file": "src/bin/desktop/run_sets/source.rs", + "bytes": 9660, + "sha256": "39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf" + }, + { + "file": "src/bin/desktop/scene_handles.rs", + "bytes": 35097, + "sha256": "4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22" + }, + { + "file": "src/bin/desktop/scene_ui.rs", + "bytes": 38145, + "sha256": "cc866e716876dfee8c9245c2473426c7c3253b2b0787b49b907202a6e1402557" + }, + { + "file": "src/bin/desktop/scene_view.rs", + "bytes": 37630, + "sha256": "d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f" + }, + { + "file": "src/bin/desktop/settings.rs", + "bytes": 46529, + "sha256": "72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4" + }, + { + "file": "src/bin/desktop/setup_ui_tests.rs", + "bytes": 32765, + "sha256": "db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900" + }, + { + "file": "src/bin/desktop/timeline_ui_tests.rs", + "bytes": 18157, + "sha256": "606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b" + }, + { + "file": "src/bin/desktop/transport_view.rs", + "bytes": 6248, + "sha256": "e8c3d5cc588d4efac9918544fc5012696a05bdfa7adcba7ce18b07c5a260f6b8" + }, + { + "file": "src/bin/desktop/viewport.rs", + "bytes": 47028, + "sha256": "ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3" + }, + { + "file": "src/bin/rheon-ai-compare.rs", + "bytes": 5924, + "sha256": "00000ec93161c04d9ff0515e5ae2ab6855a5467f56f23a4d6fc6c1059ef8ada9" + }, + { + "file": "src/bin/rheon-desktop.rs", + "bytes": 165398, + "sha256": "6e70c7957357611fa4c0610dddd4f1673544882821b9dab47b481dc2c394eff4" + }, + { + "file": "src/bin/rheon.rs", + "bytes": 11640, + "sha256": "5116a121256c8bb57d086caedbda8151f3593cb99d3e84b1d04fea25b12dfb51" + }, + { + "file": "src/box_flux.rs", + "bytes": 17346, + "sha256": "554b3cdc916e7527bbfdbd6a54b8cf7e27549c3c661015b259c8b2d18784bbb0" + }, + { + "file": "src/box_flux_step.rs", + "bytes": 5487, + "sha256": "e2ef3afcd8adffe0f79c0030eb56e5bb55943775e474a1dab2a7fe78ac38441b" + }, + { + "file": "src/collision.rs", + "bytes": 12262, + "sha256": "101c91ced5efc250f3a12bc47d5bbf04b812096caf8580c0229362e959839115" + }, + { + "file": "src/column_mac.rs", + "bytes": 35637, + "sha256": "40b86c5bab2d458c8b31b1f8def21c5f715acecb37813fb57417f7fe363aca84" + }, + { + "file": "src/column_momentum.rs", + "bytes": 22078, + "sha256": "373f63ac001fe8e9f8017a912033b3cc431169c6e4c86c2e6b514ee945f1f9b0" + }, + { + "file": "src/column_shear.rs", + "bytes": 37106, + "sha256": "bd2a20a11f927c2a886b524a4a45791100f9c3e8119b7648153b7775cb6a3299" + }, + { + "file": "src/column_surface.rs", + "bytes": 15155, + "sha256": "cd58d4d7d1c3d94092374ebd65fd191e3cf58f7ae68caeaf30ca15d36df666e0" + }, + { + "file": "src/compressible.rs", + "bytes": 29786, + "sha256": "dc0f2a3a8ebcb108c6a094ec59a467f1390b846dd27c505b16248a64dc1031b3" + }, + { + "file": "src/coupled_discrete.rs", + "bytes": 61268, + "sha256": "b296021b8dd2f763b6eeab1e49e78bf06ec2ec801ac9595441e9e3920235d8e7" + }, + { + "file": "src/dense3d_sequence.rs", + "bytes": 20084, + "sha256": "90e9249fd82cc829a84cea9162d680971e660bfc6c32619355b353d17b25a431" + }, + { + "file": "src/emitter.rs", + "bytes": 16400, + "sha256": "57ea2badaeb329fe91647df98be60bff8dbe693e41076979eda4b79ad8f48f84" + }, + { + "file": "src/emitter_controls.rs", + "bytes": 3358, + "sha256": "47d0b180dafc7e57ad1b205f2a12c5a472a78c1d60a7c3f7521ec470211a89c4" + }, + { + "file": "src/export.rs", + "bytes": 4681, + "sha256": "afe2b79d11f1b8c0ccd16dcaae51a22c8d35ba2e7a23c362729fe907b208f0d3" + }, + { + "file": "src/fitted_height.rs", + "bytes": 48528, + "sha256": "0181db7ffbad932b7e3492ce1a8e260736389e4ded3472c81fe2697861f6777a" + }, + { + "file": "src/fixed_bottom_ale.rs", + "bytes": 28202, + "sha256": "3cc724d376d115fccd8b6c658d7f26b6436f4d13d105a5ae8416fd9c04b17a47" + }, + { + "file": "src/force_field.rs", + "bytes": 14833, + "sha256": "adecbc5f92b89179a2e38885be2acbcddd42df40061f00ffa5d758224ed045a5" + }, + { + "file": "src/forces.rs", + "bytes": 10490, + "sha256": "7b3b48c0813c995a937a44ca1b624411c59dde6419b62041b67456cde2d692d9" + }, + { + "file": "src/free_surface.rs", + "bytes": 3639, + "sha256": "f43fd9a71da6aac985dfcfb6cc471318227c1727d6e0dad8e2a5feb5b3728a8e" + }, + { + "file": "src/geometry.rs", + "bytes": 11743, + "sha256": "712d04f27d55d7e77f17effefbf2660ca3b8a000de5b7aa51b42b89fe5d3b634" + }, + { + "file": "src/lib.rs", + "bytes": 8361, + "sha256": "30f5b78a98f6d0c010955a25fc00cc2de4aadf159d260e95afd3f3143dc6f9c3" + }, + { + "file": "src/liquid_step.rs", + "bytes": 32240, + "sha256": "f99dce6a99f6af8a9aef2436af7f33de58dac3be5998a48e9187fbdf2acc3f93" + }, + { + "file": "src/liquid_volume.rs", + "bytes": 29752, + "sha256": "24623038f62261ce6a17b034e9b6d82c5f0aba02dd0df7aad174c885ed2a8c96" + }, + { + "file": "src/material.rs", + "bytes": 7103, + "sha256": "49874ff972f16a972d706236b34e62f83e994c57e39b642c6d94a1a6f4b7fb66" + }, + { + "file": "src/material_session.rs", + "bytes": 10964, + "sha256": "4e18e446ad82f10523f72bae1b7cc6007f2ecf5ad05407bb666be3e2ae9be960" + }, + { + "file": "src/mesh_collision.rs", + "bytes": 19695, + "sha256": "71503d2c5939dbb59946bc157eedafecdedd0831f1e81d29e794fba7290dfd24" + }, + { + "file": "src/mesh_import.rs", + "bytes": 24294, + "sha256": "3e8f2d79e59b3f27c7ffeeacc83d9b7d08faafa2fffa7de81dfd8665b36a6890" + }, + { + "file": "src/motion.rs", + "bytes": 6031, + "sha256": "8ab9f26199f36d76cb2f5e4cb58d40905205f0d6fc90988549f7e389c489a3ea" + }, + { + "file": "src/observation.rs", + "bytes": 6669, + "sha256": "2c6d4cd75cdb0f2d560d0f79df4e53914e10f340de0740b939311d8cc35d5dee" + }, + { + "file": "src/obstacle_pressure.rs", + "bytes": 24077, + "sha256": "b45262632483ce08773924b407eeffbe99bd62663094ce6fd71b24d132026b07" + }, + { + "file": "src/obstacle_shear.rs", + "bytes": 16421, + "sha256": "745da07d5e949276a2e0cb7ac4bdde4aedf557bf6189d5a51581d8ce79b1dc1f" + }, + { + "file": "src/operator.rs", + "bytes": 29555, + "sha256": "cd98684d5204908e5344b2628a8f93ea2a55a90c6678e03a71f2d75ab1f2be38" + }, + { + "file": "src/parallel.rs", + "bytes": 5488, + "sha256": "ee5420673a59ae4fb247b3542878e74902f7a87056744d4303fb400be074677e" + }, + { + "file": "src/particle_fluid.rs", + "bytes": 57483, + "sha256": "538c498122bbc1045ae59d7363ff87e1dd9a40003fb48eeae835228ad4c65893" + }, + { + "file": "src/pressure.rs", + "bytes": 28790, + "sha256": "39ddb79dd3de196e83545679e556c0cd8ce9afae9db9282161f54b8f22f713a5" + }, + { + "file": "src/pressure_session.rs", + "bytes": 11353, + "sha256": "ae687a8822dd6cfeadf5dc6e13e7983d8d1afcdf9e5db1857d4dce64cd0e3fb8" + }, + { + "file": "src/provider_step.rs", + "bytes": 3623, + "sha256": "79167b70a8d164a2367f43816feab443eefb41f23bc4bd940716dd5fd32d495b" + }, + { + "file": "src/recording.rs", + "bytes": 38908, + "sha256": "47b2b24bbe9af777ceafdefa6f0b82200a60cea590d0b64f326de1d50c6d7d2d" + }, + { + "file": "src/recording/carrier.rs", + "bytes": 13424, + "sha256": "5a9b10b929f650423ad9e58bd7047bfe80cc7bf18138d74bd628c2f3f17af711" + }, + { + "file": "src/recording/comparison.rs", + "bytes": 27711, + "sha256": "5a82201f4583cf9eb4cc38d43c7ee88bc54d10a84df46272c2dd84091aa4a307" + }, + { + "file": "src/recording/diagnostic.rs", + "bytes": 67253, + "sha256": "279aff886f1354af203429a2e43cce73cdb485ee5ddd90201d1498e5e7872b7f" + }, + { + "file": "src/recording/force_fields.rs", + "bytes": 20485, + "sha256": "2cf3ec9ec191c35c814d48ace301feaccd6cc73311a66970c9d112dad432c4a9" + }, + { + "file": "src/recording/material.rs", + "bytes": 16944, + "sha256": "e72e256aa3af46fef9002822de5281caf1de66ba070eb2109f77b083e20ae6ce" + }, + { + "file": "src/recording/provider.rs", + "bytes": 48561, + "sha256": "980ea5af7094016b13e7d0f39110aa2ff673b0173416e0fe583e6266b7c27f57" + }, + { + "file": "src/recording/provider/comparison.rs", + "bytes": 7457, + "sha256": "90c1230c2cbfb2415c74e5d3c9b66e74f68c7b2096bf4063c5513ac2c6feaa75" + }, + { + "file": "src/recording/provider/runtime.rs", + "bytes": 31624, + "sha256": "d7b2a1f7cfa866b33887f7c8f804999058d6e70a1c9d71421372d151152ebccb" + }, + { + "file": "src/sampling.rs", + "bytes": 6347, + "sha256": "6bf158f71f795078b5a88a5662ec2169693508e555f0f98d539657873aaabbcf" + }, + { + "file": "src/scheduling.rs", + "bytes": 15224, + "sha256": "21fa031c06eeff8c2f722beb5553af9ba86d0afab479f4036916480338dc1411" + }, + { + "file": "src/session.rs", + "bytes": 17759, + "sha256": "367c370e2257e35de09943e8dc3e1257b994c5e01d1409af1a989682f41c583e" + }, + { + "file": "src/session/configured.rs", + "bytes": 10308, + "sha256": "d99ae990e9c7ddc956f95f414a25832411a4dc84ab434df95ff9f8f68cf52a83" + }, + { + "file": "src/sgs.rs", + "bytes": 14651, + "sha256": "0b4140af63144df8ff1839782b3c70295c18b96decc67aba348a37a33e9f996d" + }, + { + "file": "src/simulation.rs", + "bytes": 48293, + "sha256": "c08ede6330c4bf6ed7e858e556806ec80628522ceb42286cf52714bf7430c9a1" + }, + { + "file": "src/solver_provider.rs", + "bytes": 10991, + "sha256": "0ab7db08a9016e73d664c730e4ff513d42599a84c680ea061e6604859432a5a5" + }, + { + "file": "src/solver_provider/configured.rs", + "bytes": 9193, + "sha256": "124ca65dd1dec3a8a89043198fb085453a42ca8aff0dc87c67b04b6a986109be" + }, + { + "file": "src/solver_provider/context.rs", + "bytes": 6332, + "sha256": "763debfefc6f9375849c8f503995e20660fc25efef9e827d5942d980e14fe693" + }, + { + "file": "src/solver_provider/modal.rs", + "bytes": 6061, + "sha256": "c597450ad73a4dede64a1fedcc65a0b4d5551f594f29b2d0cf7c85b0e2e14796" + }, + { + "file": "src/static_obstacle.rs", + "bytes": 18098, + "sha256": "df50f3761fd7258ca42026ba852d2c326a1a5efa42daa4021967b799493b1080" + }, + { + "file": "src/tracer_barrier.rs", + "bytes": 9936, + "sha256": "1007f35400b5e1b3206653b9f9ccaafbc169617ad896aec54f94fc792eba2fae" + }, + { + "file": "src/translated_viscous.rs", + "bytes": 29473, + "sha256": "6fa240053738411bb93b3e365def39fdc8a93597cd0c0523dcd1af39cbc68163" + }, + { + "file": "src/viscosity.rs", + "bytes": 15726, + "sha256": "3cb7643d321fa94957fafdc101abf0ee9ef273f99c9a512938124e72f32178aa" + }, + { + "file": "src/vtk_export.rs", + "bytes": 21304, + "sha256": "56c3f03d957df0a7fd0c00a1af229f47f1c21cdf53cbf3770656a9bb0b66735e" + }, + { + "file": "tests/advection_contract.rs", + "bytes": 4293, + "sha256": "2e6c78e51d457bfe0cda4972dbfec5677e9e3a5e4f42a8647aec8aa1f4b73f2d" + }, + { + "file": "tests/aligned_stokes_contract.rs", + "bytes": 15996, + "sha256": "7b43a757245edc097874bfd23ec0690d1d4a8df9eb6186ab86e843720dc7a687" + }, + { + "file": "tests/aligned_strain_contract.rs", + "bytes": 12213, + "sha256": "d2270f86bd04c36501b59f01772656bda264d2c70b8613429701a268a305da16" + }, + { + "file": "tests/box_flux_contract.rs", + "bytes": 21542, + "sha256": "d3a23e9ae3973044183d22c5dbc9b2a00c2d0d731cae1777426337310949133d" + }, + { + "file": "tests/box_flux_step_contract.rs", + "bytes": 23582, + "sha256": "4adc5ddf0e878cffc3e70b47bcb0f98cad07aef4489a41f9fa7d1695304bd94f" + }, + { + "file": "tests/cli_contract.rs", + "bytes": 8980, + "sha256": "83d3c9a839dd2c722b3c509e051350a6ca1c754cbe0ae5dbb521e7d3346c6aef" + }, + { + "file": "tests/collision_contract.rs", + "bytes": 14667, + "sha256": "8625b6a0d01271b90f17fd22b0ea4f5d0869827fad6537720fb631ce565367ac" + }, + { + "file": "tests/column_mac_contract.rs", + "bytes": 23553, + "sha256": "3fc23b6ff935696bb96082e824cc1a5c2cfe32821559074f6ec00b3b8000afdf" + }, + { + "file": "tests/column_momentum_contract.rs", + "bytes": 14152, + "sha256": "4ea859692dc437e98bdce6fc3fdfb55a8821ffc52699c8a7596ddc7d75e8aec7" + }, + { + "file": "tests/column_shear_contract.rs", + "bytes": 48884, + "sha256": "466dab4d7dd6cfa464de3428da8b0b9aadfb3a2699810ca302e9bec90db62f12" + }, + { + "file": "tests/column_surface_contract.rs", + "bytes": 20247, + "sha256": "515f39b3bf06fe23c1115201cefa1d4f1d69660ce4298daf3d4b7e16f5844759" + }, + { + "file": "tests/comparison_archive_contract.rs", + "bytes": 21129, + "sha256": "ddb88a1faba015e1f636d66d6dbc8a770a1875ad52ada6d6a0a773b5c0e4f0e2" + }, + { + "file": "tests/comparison_field_view_contract.rs", + "bytes": 24168, + "sha256": "2bfcf188fbde8ba9908c90365eb10a3e708853d0a928267541a5cfb5358b22f2" + }, + { + "file": "tests/compressible_contract.rs", + "bytes": 17244, + "sha256": "9f1582b436e268a7bef6d42dccc3ff0754b963942301eb26442d7f33790d5b7d" + }, + { + "file": "tests/configured_archive_binding_contract.rs", + "bytes": 81349, + "sha256": "821ddd7d173c84453650d2cde938bf024d907e2f062a894ea2b182e44ac56932" + }, + { + "file": "tests/configured_provider_session_contract.rs", + "bytes": 28757, + "sha256": "ba4a8f26fbf0324d705a80a4b108b1e9d09a293ac2eb29a8278fe01f65dc5a00" + }, + { + "file": "tests/configured_recording_comparison_contract.rs", + "bytes": 41913, + "sha256": "8205a1b5ddb1248d545edc27d4b23f27fb89717f311ec336d01bb3202591f2fa" + }, + { + "file": "tests/configured_run_sets_contract.rs", + "bytes": 24002, + "sha256": "29b29888112126677262bdd05792cad70170338eaac8c4767fc2ab0292a9e927" + }, + { + "file": "tests/coupled_discrete_contract.rs", + "bytes": 8944, + "sha256": "3a730e8dbeaf8fd81a19eee5717b4719f62af83e42c395691535a1ed6f1a0116" + }, + { + "file": "tests/dense3d_controls_contract.rs", + "bytes": 15542, + "sha256": "678058ce656cde9d0b8c5f3ac9de4da5a81ca3f76d5003c1954ff3929d964625" + }, + { + "file": "tests/dense3d_sequence_contract.rs", + "bytes": 19273, + "sha256": "016ba99a4a234ced02a1613d5cc4af6072f3e070f185bf8ad25ca73202b128a7" + }, + { + "file": "tests/diagnostic_pressure_oracle_contract.rs", + "bytes": 10458, + "sha256": "b492194a384779fd15ba967bac93a5068d1635d5ff5b443d13a31252716134d7" + }, + { + "file": "tests/diagnostic_recording_contract.rs", + "bytes": 54080, + "sha256": "3cd9e0a01916ed416cffd93f96aa463447beed404f202e3e2f5783c76122b758" + }, + { + "file": "tests/emitter_contract.rs", + "bytes": 3338, + "sha256": "9288b53744ff31fb4e338be13d8b4e9e68ea58aec2e78a15d5aa8850da0a9d4d" + }, + { + "file": "tests/emitter_controls_contract.rs", + "bytes": 11641, + "sha256": "7b3ad890d44d70652c04e119156dd6ecc32cb79587c87e49b1fd2fc72f6caa66" + }, + { + "file": "tests/export_contract.rs", + "bytes": 1199, + "sha256": "299ace3dc49a9990b52cd2ee55c974769c3acda5fc9ce43331fdb0cbfd34c070" + }, + { + "file": "tests/extruded_third_contract.rs", + "bytes": 10602, + "sha256": "19968c75c9dd95c1c19cc2a4f6a605d4a047bc20290562fe693fc7e8b628fbe2" + }, + { + "file": "tests/fitted_height_contract.rs", + "bytes": 21420, + "sha256": "391e6d3c992442a5a3a2e38f4d1304a20602047808831bd873567b1d8224a61f" + }, + { + "file": "tests/fixed_bottom_ale_contract.rs", + "bytes": 9344, + "sha256": "006968122943cd48290f302cdc33115d7c40058d992f8f38df1d5fbf28f57289" + }, + { + "file": "tests/force_field_contract.rs", + "bytes": 9056, + "sha256": "1f97de31f8db1db38ff581e0ce702ffad7c16284fb29c39e8d61fc51ac9c645c" + }, + { + "file": "tests/force_field_solver_contract.rs", + "bytes": 7250, + "sha256": "9efdf712baaa8e662e77339a81074751ed10ef01b9675f99351fac2a662b41fc" + }, + { + "file": "tests/force_recording_integration_contract.rs", + "bytes": 15788, + "sha256": "e9ca49fdeb0f474cdcddb40930c3773dc71762a709ac7dec6200fd5db31859a7" + }, + { + "file": "tests/forces_contract.rs", + "bytes": 13633, + "sha256": "b8a5da48feab2eef82cc1600945cc30ff15082c0b447d8b5eb0c635821cf27eb" + }, + { + "file": "tests/free_surface_contract.rs", + "bytes": 16199, + "sha256": "8f9584c26f7a816bb53f15bb70ddec168c0949adf9ad7847ad2edcfb0af40e9d" + }, + { + "file": "tests/geometry_contract.rs", + "bytes": 5402, + "sha256": "87b34154f609767f1521a57531f6f45df4d965685113dcb43806069299865f4a" + }, + { + "file": "tests/implementation_contract.rs", + "bytes": 6301, + "sha256": "e8b3b88536bd5d38dfda7d1454e916277e5314bbb9410bda81630faffe76c621" + }, + { + "file": "tests/liquid_scale_division_contract.rs", + "bytes": 2797, + "sha256": "dede02f6a5fc9eff67d87814b1845bfc6295c7a22098121217fee70822a4c3fb" + }, + { + "file": "tests/liquid_step_contract.rs", + "bytes": 15658, + "sha256": "e1aa67f673de131f97a1be651221db3f74a8beeed105076532ceeac2b92b5b12" + }, + { + "file": "tests/liquid_volume_contract.rs", + "bytes": 28424, + "sha256": "9eac2c3feedf24efd3e34f1651ead5ea14f036dbfb238af829650d5b77006ec2" + }, + { + "file": "tests/material_contract.rs", + "bytes": 10486, + "sha256": "c5a1e89205b6cb65df69a1c10fa905f85d5efa6bc11c591211d509cb40161257" + }, + { + "file": "tests/material_reference_contract.rs", + "bytes": 15134, + "sha256": "9db6a8cc0558b4b04b4039ab8b7c74bd57c2f69a15d05b2b553713713a8f387b" + }, + { + "file": "tests/material_session_contract.rs", + "bytes": 5880, + "sha256": "9fadbca17897a93e9dc95be24a42c5181bfeaaf35aad650ea4bf6f477b9e580f" + }, + { + "file": "tests/mesh_collision_contract.rs", + "bytes": 10440, + "sha256": "e8977a66763745cfb807e9bcae392be3aa39ebca113aa3db4c4d4eb39447c8ca" + }, + { + "file": "tests/mesh_import_contract.rs", + "bytes": 12926, + "sha256": "a568e510cb457f8d90bffd2c99eb1c05d64ce5ac4c34949a58479831ffad3da9" + }, + { + "file": "tests/native_comparison_oracle_range_contract.rs", + "bytes": 10974, + "sha256": "290def29e35b9597d1c56757f5fb70d9ac100d95ca67b616c54533d31f8e0f82" + }, + { + "file": "tests/observation_contract.rs", + "bytes": 2355, + "sha256": "54a89d2df244839dec1931cde92117cf1bb832ffb0b6eb2ca32ca9ec28a8f7ed" + }, + { + "file": "tests/obstacle_flow_contract.rs", + "bytes": 17654, + "sha256": "7e5318ca45b6932818accfc748cc70ae6cf1719f6047c2d2f9343eb8cedcd020" + }, + { + "file": "tests/operator_contract.rs", + "bytes": 3742, + "sha256": "b0ea207c02df69aed1f5f3eb6abda8fdeb245b271c325b383c3748d945c5095e" + }, + { + "file": "tests/parallel_contract.rs", + "bytes": 6972, + "sha256": "b91c7c87698c1a7b1d3b92c7b252781d96616df37c8a6ef391d4229a251668f2" + }, + { + "file": "tests/particle_inspection_contract.rs", + "bytes": 1390, + "sha256": "9e36d134d2391786457ff23bb20df63a5aadf29a8ae508cffec2de824528bc93" + }, + { + "file": "tests/plate_coupling_contract.rs", + "bytes": 10763, + "sha256": "3c413b5c903194ba8f1a83970275d066ba93a5568f0d43a27f93788f3c4af5fd" + }, + { + "file": "tests/pressure_context_contract.rs", + "bytes": 4495, + "sha256": "244afca41c35a929588f5dd0168ae682b1ae0457f0edc3f821b95ed93d7f2f36" + }, + { + "file": "tests/pressure_contract.rs", + "bytes": 10147, + "sha256": "f343f7f23965d963df8d734d624c63b12acaf165936ba2635e1b71816ee16c91" + }, + { + "file": "tests/pressure_session_contract.rs", + "bytes": 7663, + "sha256": "a2471a0faa91bac0acd4c824ee4b1175fe6f827357246218684ae47104aea0ce" + }, + { + "file": "tests/provider_step_contract.rs", + "bytes": 14763, + "sha256": "116c03830ed617e585a6eabe1dd9d8e826ed833039e4397de565d218ec9852e5" + }, + { + "file": "tests/recording_carrier_contract.rs", + "bytes": 12906, + "sha256": "8a3a5cd307b9b5e2b646165502029018eeb9e36268986b679094a9a1e43dfba0" + }, + { + "file": "tests/recording_comparison_contract.rs", + "bytes": 40026, + "sha256": "4c27fc27b241c033eeae0903dd143fe1800cc8ea5ab9829c8b13e22105ee133b" + }, + { + "file": "tests/recording_contract.rs", + "bytes": 30135, + "sha256": "3db4e56207de52576aae5838d30e02894590ba843d1af8d10e5e7fd5a772ee8a" + }, + { + "file": "tests/recording_provider_contract.rs", + "bytes": 59426, + "sha256": "0766d5d9ac55fd8102c23b9492e11d41e4caa931249531a8b36824b2a97039c0" + }, + { + "file": "tests/recording_provider_runtime_contract.rs", + "bytes": 47395, + "sha256": "7f02cf7510be8bd9bca9b3af380e80401ead89ee9b5c2e2561de65e31c5f6cd1" + }, + { + "file": "tests/sampling_contract.rs", + "bytes": 2908, + "sha256": "3e99ff52088aa37d4043453a57c82bbe6de0da84e990796df76a3453f1ae004d" + }, + { + "file": "tests/saved_comparison_ui_contract.rs", + "bytes": 24025, + "sha256": "f3bf401a00ccb9ba67de70ff72c6c3a0b7216525073a6f6c8239ec022e0bec1f" + }, + { + "file": "tests/saved_run_set_contract.rs", + "bytes": 34203, + "sha256": "6e001075a4307da0e51df04c4521bc8599194ce06fbf010c08e34bab8f693ef9" + }, + { + "file": "tests/saved_source_contract.rs", + "bytes": 21328, + "sha256": "39cb5152583c1662f8dfb71217409025084c4e05be1a18e1b7b3912fa4d21ca5" + }, + { + "file": "tests/session_configuration_contract.rs", + "bytes": 8504, + "sha256": "4f53c8d35eeb44819791341b8ce1a80421d9562ab8413f528a3234da233632f7" + }, + { + "file": "tests/session_contract.rs", + "bytes": 4928, + "sha256": "50f7a8981d1bc22ad63f990b01027b0ae26c4dc01192dc5dd7d9723f7e44da68" + }, + { + "file": "tests/session_interval_start_contract.rs", + "bytes": 8936, + "sha256": "4cdc0a85d21d77784ef142ba04f1ef20fb0333555d7bbb15b43465f475934769" + }, + { + "file": "tests/simulation_contract.rs", + "bytes": 7545, + "sha256": "f81fb0696307aa07abba0d8b44316430aeab19317c57d38db3222cdc9e33ddc6" + }, + { + "file": "tests/solver_provider_contract.rs", + "bytes": 11506, + "sha256": "0c063df217150bf407af3f6af9d39101bc37aa3192e00e0833b43042ebc06143" + }, + { + "file": "tests/static_obstacle_contract.rs", + "bytes": 9794, + "sha256": "fcc7546886b865ada1641dd7ee3549534f2273d908c19047d886c49a048157de" + }, + { + "file": "tests/submarine_stokes_contract.rs", + "bytes": 15839, + "sha256": "20cb58baf441b56e7b5f72b9424de265c7bf656bb2c3532d4718220b8c291ce2" + }, + { + "file": "tests/tracer_barrier_contract.rs", + "bytes": 15648, + "sha256": "433edf41b549171f3a3c412e6d9233345f4ca8f45e6f5ebe5468638d3db9aca8" + }, + { + "file": "tests/translated_viscous_contract.rs", + "bytes": 8782, + "sha256": "d6fda116af1ae38a3e9216b97136562c144bfe1a5269734d002c3117c9b65e79" + }, + { + "file": "tests/translation_contract.rs", + "bytes": 12063, + "sha256": "e1fd504cb399b313bbb1df752f79e72cb51556c5af6d85dbb91cb5c690fb5c8d" + }, + { + "file": "tests/viscosity_contract.rs", + "bytes": 16747, + "sha256": "b716ef20283784d9a61bc95702c97c30c8e7d00c80f95530c46d27e667586c9d" + }, + { + "file": "tests/vtk_export_contract.rs", + "bytes": 7406, + "sha256": "3df0cd4bfe900adb855fa65d9ec267e43544961fd5608d4f1ab24258c6a44532" + }, + { + "file": "tools/production_recovery_probe.rs", + "bytes": 7080, + "sha256": "70e248db8c7cccb9a70ce362de29db268a708574920345037887bc1e7110eae3" + }, + { + "file": "tools/session_recovery_probe.rs", + "bytes": 3516, + "sha256": "eab066cb8dff750893c8a2eb9b7bd4efb22f1f4a18daf4fd49106a65bd7743f1" + }, + { + "file": "validation/cpu-cfd/src/main.rs", + "bytes": 8445, + "sha256": "339ee7d2caad1d5350a5cda8bb4968c201a3d1cb62d301ed7b1eedd2d72a32d6" + } +] diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 992a2c76..13a0bdf4 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -513,3 +513,26 @@ once each under10s/5CPU/512MiB/1MiB wrapper; no timeout/signal, no surviving chi 199-name enumeration, check/strictClippy/fmt/diff pass. No numerical/model/SQLite or native execution. Preserve failed native evidence. This enabled path is distinct from disabled synthetic presentation; native picking/handles remain UNVERIFIED. + +## T5 real-Worker runtime/SQLite test preparation —2026-10-21 + +Separate after published1b37774 focus fix. Added2ignored numerical/SQLite bodies, +real Request→Worker::start with existing scopedRayon1, join beforeassertions, +1publicstep each: native8cells/max100pressureiterations, material64particles/max16 +preparations. Source guards effects/custody/coldreadback and rereadsmetadata after +separatedraftmutation. Native rerunclone is prepared/non-executable; materialonly +retainedRequest+coldForceScene because no serializedcoldrerunAPI exists. No secondrun. + +Complete helper and plan reviews admit preparation only. List-only compile201, +ignored-only4(2new+2legacy). Corrected privateSnapshotStage import and wrong total2 +count assertion; preserved failures, no testbody executed. Check/strictClippy/fmt/ +pinned structural plan gate pass. Source278-file manifest603a0edf…5fbafc; +compiled testSHAaf2da153…727b5. No numerical/SQLite/Worker/model/native invocation. + +Actual contract proposes30s timeout+teardown/20CPU/512MiB/1MiBlog+perfile, fresh +DATA/capture separation and retained postexit failure inventory. Known7native/2 +material filenames, SQLite transient peak explicitlyunsealed;8file8MiB final +assertion is not a hardaggregatebound. No newsecurity/mount/cgroup requirements. +Future typed-effective-target gate-removal scope independently reviewed, no code +until boundedruntime+reachablegatednativeacceptance. ActualenabledUIlaunch needs +separatecandidate-sourcevalidation aftereligibility. Productiongate stays. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index e764005b..ff857eb0 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -1,8 +1,8 @@ # Desktop scene authoring and compact playback **Plan status:** `Active` -**Current phase:** T5 desktop custody source implemented; bounded validation pending -**Next slice:** independently reviewed bounded native-carrier/material runtime and archive probes +**Current phase:** T5 ignored worker/archive tests prepared and enumerated; no execution +**Next slice:** separately admitted exact native-carrier/material worker/archive validation **Acceptance status:** `blocked` **Execution ledger:** [execution-ledger.md](execution-ledger.md) @@ -236,9 +236,11 @@ Retained tree/properties and viewport share one render-frame track/document; track intents paint consistently next frame and clear reused-ID selection. No numerical/SQLite/runtime or new native acceptance is claimed. Thirteen exact -pure/Context tests pass after list-only enumeration. Future two-case validation is -specified in [bounded contract](reports/runtime-force-validation-contract.md); -its ignored test bodies are not yet implemented or enumerated. Keep the production +pure/Context tests pass after list-only enumeration. Two ignored worker/SQLite cases are now compiled and individually enumerated +(201total), with [bounded contract](reports/runtime-force-validation-contract.md) +and independent worker-test reviews. Neither case has executed. Material rerun +coverage is retained typed Request plus cold force-scene association; a production +serialized material cold-rerun API does not exist. Keep the production force gate until separately admitted runtime/archive evidence qualifies this source. Earlier native425a986/8148fa2 and synthetic harness artifacts retain their own exact source/evidence; none accepts this changed desktop closure or genuine cancellation. diff --git a/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md index 6e32d5d6..82936f62 100644 --- a/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md +++ b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md @@ -1,123 +1,180 @@ -# T5 bounded desktop validation contract — proposed, not executed - -Status: source integration is gated. This document specifies the next exact cases; -those two numerical/SQLite test bodies are **not yet implemented or enumerated**. -It is not an executable admission, workload authorization or runtime result. The -current executable probes are the seven pure tests in force_runtime_ui_tests.rs. -No force-enabled production launch is admitted by source-only evidence. - -## Ownership and prerequisites - -Use canonical backend core at43ec505218c43594641ecbb80873bec8c88d7d2c; fetched -backend a4326a78a747bc868874fd16e434d780fd5a6d35 differs only in its own admission -review/inventory. Preserve legacy hashes. UI owns Request/carrier dispatch, -Inspector begin/record, frozen desktop intent/binding and retained presentation; -backend remains owner of force evaluation and recording/accepted solver custody. - -Before execution, implement the two separately ignored cases in a separate desktop -test module, independently inspect their complete helpers and enumerate exact names. -Seal the final commit/tree, complete affected-source SHA256 manifest, Cargo.lock, -Rust version/feature configuration, test-source bytes and exact compiled binary. -The current197-name binary does not contain these cases and must not be represented -as execution-ready integration evidence. Compile/list-only does not step a solver. -No tuning, retry or fallback suite is implied by this contract. - -The execution owner must revalidate its existing supervisor and fresh bounded -recording filesystem. Do not provision mounts/cgroups, change delegation or reuse -a previous output root. If an existing suitable bound is absent, return the exact -missing prerequisite. Existing live cancellation stays parked; this contract does -not replace its containment requirements or supervise a native UI session. - -## Common bounded execution envelope - -One exact ignored case per disposable process, serially, libtest threads1. Desktop -features include rayon: use the production run_owned scope with Request.threads=1; -this makes one owned Rayon pool worker, rather than claiming parallel is disabled. -No Worker::start, GUI/event loop, model/provider, child command, emitter, collision -mesh, observer, progress/failure gate or public network input is present. - -Require the execution owner's already supported controls before dispatch: - -- wall30s, CPU20s, address space1GiB; TERM then owned process-group KILL after1s; -- per-file1MiB and aggregate fresh root8MiB, aggregate captured output1MiB; -- root created once without replacement by the supervisor, exclusive to this case; -- no automatic success/failure/panic/timeout teardown or cleanup; retain database, - sidecars, manifests, stdout/stderr, source/binary seals and disposition; -- enumerate all retained files/bytes and SHA256 after the child terminates; check - no owned descendants remain. Do not infer measured peak resources from limits. - -The test helper must accept an explicit absolute supervisor-owned root with no -fallback to temp_dir or normal recording_directory. It must not delete existing -paths or use a Drop cleanup guard. Every path is inside that fresh root. Stop on -any unexpected selection, file, resource-bound failure or timeout; preserve evidence. -These are maximum requests, not promised observed consumption. - -## Case1 — proposed exact name +# T5 bounded worker validation contract — prepared, NOT EXECUTED + +This amendment supersedes the earlier direct-loop/1GiB proposal reviewed at5eab3ac. +Two ignored bodies now exist in desktop/force_runtime_integration_tests.rs; list-only +compilation enumerates201 tests including their exact names below. No numerical, +SQLite, Worker or model workload has executed in this preparation slice. Source +review, final seals and separate execution admission must precede any invocation. +Production authored-force launches stay gated until runtime and relevant native +acceptance pass. Do not treat these source/compile receipts as runtime acceptance. + +## Scope and custody + +Canonical backend ancestor43ec505218c43594641ecbb80873bec8c88d7d2c; fetched backend +a4326a78a747bc868874fd16e434d780fd5a6d35 only changes its admission documents, not +core code. UI owns Request/worker dispatch/Inspector wrappers/frozen desktop intent; +backend owns physics and accepted recording custody. No legacy hash migration. + +Both tests call real Worker::start, not an alternate direct solver loop. It validates +Request, creates bounded in-memory history, starts the existing one Worker thread +and a scoped Rayon pool with threads1. Libtest also uses threads1; main/test/Worker/ +Rayon are the4 intended thread roles here, not a measured or OS-enforced thread cap. No new process/model/provider/emitter, +collision mesh, native window/GPU, observer, progress gate or failure gate exists. +The helper joins the worker before outcome assertions; normal/error/panic outcomes +therefore leave no detached numerical worker. External process timeout bounds a +blocked join. No second worker, baseline run or external retry is included. + +## Exact future execution and process bounds + +Execute each exact ignored name ONCE, serially, from the sealed test binary: + +``` +SEALED_BINARY force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2 --exact --ignored --test-threads=1 --nocapture +SEALED_BINARY force_runtime_integration_tests::material_force_dispatch_records_immutable_scene --exact --ignored --test-threads=1 --nocapture +``` + +These lines are the contract, not commands run by this task. No substring/broad +filter, fallback suite, tuning or automatic retry. Seal final source commit/tree, +affected source manifest, test bytes, Cargo.lock, Rust/feature configuration and +binary SHA256 before execution. The earlier197/199-name binaries lack these cases. + +Use the execution owner's established disposable-child supervision, with no new +cgroup/mount/permission/network prerequisite: + +- wall timeout deadline30s, CPU20s, address space512MiB, captured combined log1MiB; +- child per-file limit1MiB and core0; one owned process/session group, TERM at wall + expiry then KILL after1s if needed, wait/reap and inspect owned-process disposition; + the30s deadline excludes teardown latency and is not a hard reap guarantee; +- one fresh canonical absolute empty DATA root per case; supervisor stdout/stderr, + receipts and seals are in a SEPARATE capture directory, never pre-created in DATA; +- set RHEON_UI_FORCE_TEST_ROOT, TMPDIR and SQLITE_TMPDIR to that same DATA root; + the helper checks all three. No temp fallback or production recording directory; +- preserve root and capture directory on success, assertion, panic, timeout, signal + or uncertain outcome. No Drop deletion, remove_dir_all or test-success teardown; +- after child exit, the supervisor inventories/hashes all remaining DATA/capture + artifacts EVEN on failure (the test's inventory helper runs only on success). + Report actual disposition, bytes and any remaining processes. Limits are not + measurements. Stop on an unexpected selection/file/bound failure; preserve evidence. + +512MiB is the proposed hard cap, not an observed runtime result. Current enabled +UI tests already ran in512MiB; these bounded8cell/64particle fixtures allocate1MiB +solver arrays plus a small16MiB-capped preview plan and1MiB SQLite cache/connection. +If that cap rejects execution, retain failure; do not silently raise it. + +## Data-file bound and existing protocol behavior + +The program has no dynamic step/retry loop around public execution: each case has +one track, one requested step and at most2 publications. Source paths are fixed: + +| Data path class | Native maximum simultaneous entries | Material maximum | Bound/owner | +| --- | ---: | ---: | --- | +| intent.json, member-0-started.json, member-0-finished.json, completed.json |4|0| Each manifest <=1MiB via publish_new; no extra revision loop | +| .rheon-manifest-PID-SEQUENCE.tmp |1|0| One serial publisher at a time; an uncertain publication returns immediately rather than continuing to another staging path | +| One generated carrier/material SQLite database |1|1| max_page_count caps database at1MiB; no second archive, ATTACH or VACUUM | +| Its DELETE-mode rollback journal |1|1| One writer/one main database; per-file1MiB hard limit; no WAL/SHM mode | +| SQLite transient stores | Not statically sealed | Not statically sealed | temp_store=FILE is canonical; child TMPDIR/SQLITE_TMPDIR point into DATA. Fixed indexed/scalar queries and integrity/foreign-key checks, <=2records; readback runs serially | +| Application-owned path envelope |7|2| Each file <=1MiB under child limit; at most7MiB/2MiB for these named application paths. SQLite transient files are additional | +| Success-only observed inventory guard |8|8| Test refuses visible final inventory >8files/8MiB; this is NOT a peak/anonymous-temp bound | + +The named application paths have the finite source envelope above, not an OS +aggregate filesystem quota. SQLite transient-store count/peak is not independently +sealed here: the8-file post-exit assertion must not be described as a proven +simultaneous/open/anonymous-temp limit. Existing per-file/process/time controls +remain the proposed proportionate bounds for this trusted fixed fixture; disclose +this limitation to the execution owner rather than adding mounts/cgroups. No +numerical execution is admitted by this preparation. +Normal SQLite commits may unlink journals and successful manifest publication +unlinks its own staging hard link under the existing production protocol. The tests +add NO cleanup; abandoned paths from protocol errors remain. Do not claim that +normal transient files deleted by those existing protocols were retained. Verify +all unexpected files after any failure rather than claiming the success-only +inventory proved peak historical disk use. No spill workload/user SQL is added. + +Each publication payload <=65536bytes; constructor+accepted logical payload +<=131072bytes. The single force metadata document is146bytes by its literal codec +layout. Database/manifests' actual sizes are unavailable before execution and must +be reported from retained output, never fabricated from these bounds. + +## Case1 — exact name `force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2` -A single JacobiPcgV1 native carrier track, grid2×2×2, spacing[0.5;3]m, -origin[0;3], density2kg/m³, zero initial velocity/tracer, one requested interval -0.000001s. Frozen tolerances: relative1e-11, absolute1e-12, predicted divergence -1e-9/s, actual divergence1e-6/s, Courant1, max pressure iterations100; retained-array -limit1MiB. No source/emitter. Request preview edge2, history budget16MiB, -recording max database1MiB/record65536bytes/records2, steps1/threads1. - -Canonical scene version1 has one enabled force: id1, name“Desktop custody probe”, -translation[0.5;3]m, identity xyzw quaternion, Acceleration, Directional vector -[0.25,0,0]m/s², Global support, activation[0,∞). No configured gravity is inserted -by this test; native carrier has its existing zero-gravity inputs. - -Freeze once. Build native FrozenSpec v2 and one-member RunSetRevision v1 with no -prior attempt; bind the same Some snapshot in Request. Permit only the private -cfg(test) force_validation flag, preserving every other preflight. Through existing -run_owned → run_simulations → CarrierExecution::advance and Inspector optional -constructor/record wrappers, request exactly ONE step (max100 pressure iterations). -No retry loop around the public request. Native saved-intent fixed-dt policy still -rejects a shortened dt and retains the exact accepted archive. - -Expected evidence: one result row and progress1; two ordered in-memory frames at -constructor/accepted times; source binding, actual accepted pressure controls and -same force metadata on constructor and accepted records. Open final immutable member -through RunSetRevision/open_member, verify pin/hash/status Completed, count2, -generation1, exact stored force bytes and spec/declaration/run binding. Mutate a -separate draft after freeze; it must change neither archive nor retained snapshot. -Existing startup/finished manifests and database remain. This proves dispatch and -custody for this tiny case, not field physics, legacy equivalence or UI interaction. - -## Case2 — proposed exact name +Native JacobiPcgV1,2×2×2 grid (8cells), spacing[0.5;3]m, origin[0;3], density2kg/m³, +zero initial velocity/tracer. One requested1e-6s interval. Tolerances: relative1e-11, +absolute1e-12, predicted divergence1e-9/s, actual divergence1e-6/s, Courant1, +pressure max100iterations; retained-array cap1MiB. No source/emitter/gravity input. +Preview edge2, history budget16MiB, steps1/threads1. Recording cap1MiB database, +65536bytes/publication,2records. Snapshot frozen before Worker/output binding. + +Scene version1: enabled id1 “Desktop custody probe”, translation[0.5;3]m, identity +xyzw quaternion, Acceleration, Directional[0.25,0,0]m/s², Global, activation[0,∞). +Bind the same immutable snapshot to Request and FrozenSpec v2 in manifest v1. +Only private cfg(test) force_validation permits this otherwise gated request. + +Expected: joined worker succeeds with progress1/one row/two ordered previews; final +native member cold-opens Ready, verifies SQLite status Completed/count2/generation1, +pin/hash/spec/declaration/header and both exact force documents. Interval pressure +is present from start0, all8values finite. Predeclared coarse effect guard: +cell0-gauge p[index] is within0.05Pa of (index%2)×0.25Pa, using dp=rho*a*dx. +Zero force/metadata-only dispatch cannot pass this20% guard; it is not a pressure +precision or general scientific acceptance test. Native fixed-dt policy remains: +accepted dt must equal1e-6; shortened dt refuses and preserves the actual archive. +Preview/result times must match accepted record bits and force-qualified controls. + +Prepare a constructor-zero rerun intent from the cold v2 member, remove its attempt, +retain exact force bytes and validate. NO second Worker/step; the prepared clone still +has prior output/receipt paths and is explicitly NOT an executable rerun request. +Production rerun must allocate fresh paths through App. Mutate a separate draft, +then reread constructor+accepted archive metadata and compare exact original bytes. + +## Case2 — exact name `force_runtime_integration_tests::material_force_dispatch_records_immutable_scene` -One material track with same2×2×2 preview/grid geometry in a1m cube. Water at -293.15K; fill EmittedFluid, samples_per_axis4, max_particles64, initially exactly64 -particles, zero velocity, no emitter. Default water material and Air ambient, -ambient293.15K, gravity[0,-1,0]m/s², liquid sound speed20, wall heat exchange1, -retained-array limit1MiB. Freeze the same enabled canonical force document as Case1. -One requested interval1e-6s, at most16 internal preparation/retry attempts; no -external retry, pressure solver or max-pressure-iteration loop in this path. -Other Request/recording/preview limits match Case1. No saved native set is claimed -for material; immutable Request and version1 core force metadata own this path. - -Through existing run_owned → run_material, exactly ONE public particle step, same -snapshot for begin/step/record. Expected progress1, one result row, constructor and -accepted previews using the actual accepted state/time, cold core reader verification and -RecordedMaterial force document matching exact run-bound bytes, accepted dt/emitter/ -report controls and generation1. Assert preview/record time and actual dt association; -particle preparation may shorten dt, so do not assert a fabricated literal time or -necessarily the requested1e-6s. Both records must remain associated with that -snapshot after separate draft mutation. Preserve database/sidecars and metadata; -no automatic cleanup. Do not describe this as a material frozen-rerun format that -does not exist in the current application. - -## Subsequent gates, deliberately separate - -After the reviewed exact bodies pass once under separately admitted execution, -review their evidence before removing the temporary production gate. Native active -force launch, progress/Cancel/join, retained-versus-draft gestures, long status, -keyboard/accessibility, mixed saved members and frozen rerun still need their own -bounded evidence. Do not silently add those workloads to the above two-case budget. -The current no-force workerless synthetic harness cannot establish any of them. -Existing actual8148fa2 native validation remains owned by the current validator; -it does not cover this T5 source. JPEGs must come from actual rendered exact-source -states, labelled with revision and state, quality85, confirmed Library identity. +Same1m domain/grid2×2×2/previewedge2. EmittedFluid fill,4³=64 stationary water +particles; samples_per_axis4/max_particles64, no emitter/collision. Default water +at293.15K, Air ambient293.15K, gravity[0,-1,0]m/s², sound speed20m/s, wall heat +exchange1; retained-array cap1MiB. Same force document and Request/recording limits. +Exactly ONE public particle step1e-6s, <=16 internal preparation/retry attempts; +no grid pressure loop and no external retry. Actual accepted dt may shorten. + +Expected: joined worker progress1/one row; both SQLite records verify under the +same immutable run-bound scene,64particles, generation1, constructor zero velocities, +force-qualified actual dt/emitter/report controls. All times associate with the +actual accepted dt; no literal-time substitution. Fixed coarse effect guard: +X momentum change lies in[0.5,1.5]×(total initial mass×0.25m/s²×actual dt), with +finite positive mass/dt and finite momentum. This rejects tiny positive roundoff; +it is not a general SPH force-equivalence/accuracy certificate. Verify preview and +result actual time/dt plus64accepted diagnostic entries. + +Material has NO existing serialized FrozenSpec/cold-rerun API. The test preserves +the typed Request, checks its configuration/geometry against recorded header facts, +rebinds the cold force document and validates that retained future-run intent. +It does not reconstruct a complete executable material request from opaque header +text or claim a new production rerun format. After separate draft mutation, reread +both records and verify original exact scene bytes. No second Worker/step. + +## Gate-removal checkpoint, identified but NOT IMPLEMENTED + +Only after separately admitted exact runtime results AND relevant reachable native +acceptance on the gated source (idle authoring, controls and retained/synthetic +presentation), prepare a small independently reviewed eligibility change in main/ +SceneUi plus focused tests. A real force-enabled UI launch is a subsequent bounded +candidate-source validation gate; it cannot precede eligibility implementation. Removing just the Request gate would leave SceneUi blocking, while +removing just SceneUi's blanket refusal would silently let configured/AI setup ignore +forces. The future patch must use one typed effective-target readiness policy shared by +visible actions and last pre-output/start checks (native carrier, particle material, +unsupported); avoid independently passed booleans or a general capability framework. +That policy permits valid frozen +forces only in native-carrier/particle-material preview; preserve explicit refusal +in start_configured, Accepted Session, AI comparison, transport/provider/free-surface +and unsupported labs. Unsupported paths refuse authored metadata even if empty, +disabled or zero-valued. Saved native reruns retain stored per-member metadata and +never depend on current draft. Remove the private test-bypass field/temporary gate +only with equivalent production eligibility coverage, not a hidden alternate flag. + +Native enabled hover/close/picking/outline/handles/Escape/camera ownership/property +sync and bounded actual force launch/retained view remain separate from these tests. +Genuine active Cancel/join, mixed members, long status and full material cold-rerun +capability are not supplied by two one-step probes. Existing native owner validates +exact1b37774/timeline repair; no duplicate native session or workload is authorized. diff --git a/docs/plans/ui-refinement/reports/runtime-force-worker-test-checkpoint.md b/docs/plans/ui-refinement/reports/runtime-force-worker-test-checkpoint.md new file mode 100644 index 00000000..c4e03d14 --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-worker-test-checkpoint.md @@ -0,0 +1,75 @@ +# Ignored real-Worker force tests — prepared, not executed + +Two separately ignored bodies now exercise actual Request→Worker::start→scoped +Rayon1→native carrier/material dispatch→Inspector recording and fresh SQLite reader. +They are compiled and individually enumerated; neither has run. Production force +launches remain gated. No solver, SQLite fixture, Worker or model was executed in +this preparation, and no runtime/physics/native result is claimed. + +Exact names: + +- force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2 +- force_runtime_integration_tests::material_force_dispatch_records_immutable_scene + +Compilation/list succeeded:201total; ignored-only list contains these two plus two +legacy ignored cases. The first list failed on a private SnapshotStage import, +corrected to the public root export. A count-only validation initially expected2 +ignored cases; corrected membership checks account for4. Both failures are preserved, +and neither command ran a test body. Never invoke all ignored cases as a fallback. + +The native case is8cells in a1m cube, density2kg/m³, JacobiPcgV1, one1e-6s request, +max100pressure iterations,1MiB retained arrays. Material is64initial stationary +water particles, one1e-6s public request, <=16internal preparations,1MiB retained +arrays; no pressure loop. Both have one track/thread, no source/emitter/model/mesh/ +observer/gate/provider, two publications, previewedge2 and16MiB history cap. + +The same frozen enabled Global Directional acceleration[0.25,0,0]m/s² document +binds request/constructor/accepted record. Guards require finite native hydrostatic +pressure0/0.25Pa±0.05Pa by X index, or material X momentum50–150% of mass×0.25×actual +dt. These coarse predeclared dispatch guards are not scientific accuracy tests. +Join precedes result assertions. Check archive hashes/binding/status/count/generation, +actual accepted times/controls, preview association and exact metadata reread after +an independent draft mutation. No second Worker/step/baseline run. + +Native cold v2 member supplies prepared constructor-zero rerun intent; it still +has old output paths and is explicitly not executable or a tested App rerun. +Material checks retained typed Request intent rebound to cold force metadata; +there is no production serialized material FrozenSpec/cold-rerun API. This gap is +explicit and is not “fixed” by inventing a new format inside a test. + +Reviewed proposed envelope: one exact case/process, serial,30s timeout deadline+ +TERM/1s/KILL/reap disposition,20s CPU,512MiB AS,1MiB log/per-file,core0. Separate +fresh canonical empty DATA versus capture/seal directories; child TMPDIR and +SQLITE_TMPDIR point into DATA. Supervisor inventories even on panic/error/timeout; +no fixture cleanup. Application named-path envelope is native7/material2, while +SQLite FILE temporary count/peak is unsealed. The <=8files/8MiB final-success check +is not peak/anonymous-disk enforcement. No new cgroup/mount/security provisioning +is required by this preparation; disclose the gap to the existing execution owner. + +Independent plan/full-helper source reviews permit a preparation-only checkpoint. +Locked desktop check1.29s, strict production Clippy2.78s, formatter and pinned plan +structural check pass. The compiled test source is14735bytes, SHA256: +`da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584`. +Test executable480456920bytes, SHA256: +`af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5`. +It was only used for list-only enumeration; the shared target path is mutable. +A278-file Rust/Cargo source manifest and9logs bind this preparation. Source-manifest +SHA256 `603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc`. +Exact source/binary/toolchain/contract seals are in the evidence manifest. Any later +rebuild must be re-enumerated and sealed by the execution owner before dispatch. + +A later eligibility patch is identified and independently design-reviewed, not +implemented: one typed effective-target policy shared by visible UI and final +Request/pre-output/start checks; enable only qualified native/material targets, +retain configured/Accepted Session/AI/provider/transport/free-surface refusals, +including empty/disabled/zero authored metadata. Remove temporary cfg(test) bypass +only with equivalent inert production eligibility coverage. Require bounded runtime +results and reachable gated native authoring/control acceptance before that patch; +actual enabled force UI launch is separately bounded candidate-source validation +afterwards, avoiding a circular prerequisite. No gate-removal commit exists yet. + +Prior repair1b377742e2dd2df0cc90c99c3ad735bfd480caad (tree95a4667c675af548fcebe9acbc1de905b59902cb) +is already published for the existing native validator, with separate timeline +parentd6e23b36ab618f8873d32c89c67a62e95f775c5b. Native stall recovery/picking/handles +remain unverified here. Preserve old logs/captures/DBs, source, original M2a and all +branches. No new native session/JPEG/Library identity, cleanup or merge. diff --git a/docs/plans/ui-refinement/reports/runtime-force-worker-test-plan-review.md b/docs/plans/ui-refinement/reports/runtime-force-worker-test-plan-review.md new file mode 100644 index 00000000..907a394a --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-worker-test-plan-review.md @@ -0,0 +1,224 @@ +# Independent Worker-path runtime test plan review + +## Disposition and scope + +The amended two-case plan is proportionate for preparing actual Request → +Worker::start dispatch/effect/custody/readback probes. It improves on the earlier +direct-loop proposal by including the real owned worker boundary. Conditional +admission is for test preparation only. No numerical/SQLite execution, production +force launch or second-step rerun is admitted by this review. + +Final re-read resolves the draft conditions below. No new plan blocker remains; +full helper/source review, final seals and separately admitted execution remain +gates. SQLite transient-store peak/count is explicitly unsealed and must remain +disclosed to the execution owner, rather than replaced by a new OS quota or +provisioning requirement. + +Reviewed the parent's proposed amendment, the existing validation contract and +the temporary draft `/tmp/rheon-force-runtime-integration-tests.rs` as design +material. Temporary draft SHA-256: +`2ee5de610102351183f9c87b3da4e9f1129891c4e4731121907b184cf57724dd`. +Existing contract SHA-256: +`7327a76ef2d66525badf61d73518ad56009c2e54149ad181f703516890c3f2c5`. +Later reviewed prepared bodies now reside at +`src/bin/desktop/force_runtime_integration_tests.rs`, SHA-256 +`da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584`; +amended contract SHA-256 +`6bac634aa89780bed5dba840f9cd64aca71c314f417f28da05811d0f2a4c4455`. +This was not a complete independent audit of the production helper closure. +No build, test, numerical/SQLite/native/supervisor action or source edit occurred. + +## Draft conditions and final disposition + +| Priority | Finding | Required disposition | +| --- | --- | --- | +| High | The draft material effect guard accepted any positive net X momentum, including tiny pair-force roundoff. | Resolved: finite positive mass/accepted dt and finite momentum change, within 0.5–1.5 times mass × 0.25 m/s² × actual dt. Fixed coarse dispatch guard, not scientific accuracy acceptance; no tuning/baseline run. | +| High | Post-run `files <= 8` could be confused with a proven peak bound. | Resolved by qualification: seven native/two material named application paths are enumerated, while SQLite FILE transient count/peak remains unsealed. Existing per-file/time/process limits and final inventory are the proposed proportionate envelope; no quota/tmpfs/mount/cgroup prerequisite. | +| Medium | The helper required an empty root, but a supervisor might pre-create capture/seal files there. | Resolved: DATA and capture roots are separate; all three data/temp environment paths are checked against the canonical fresh DATA root. | +| Medium | Draft mutation proved snapshot distinction without checking archive contents afterward. | Resolved: both tests reread constructor and accepted metadata after separate draft mutation and compare exact frozen bytes. | +| Medium | A manually cloned native prepared-rerun Request retained original output directory/receipt. | Resolved: comments/contract explicitly call it non-executable prepared intent; no second Worker or production rerun claim. | + +The final native guard checks all eight finite interval-pressure values against +the cell0-gauge hydrostatic pattern (index%2) × 0.25 Pa with 0.05 Pa tolerance, +using the explicit density/acceleration/spacing. This discriminates zero-input or +metadata-only dispatch as a coarse symptom, without a second numerical run. No +claim that the expectation has actually passed or supplies scientific validation +is made; thresholds are predeclared and must not be tuned after execution. + +## Amended execution envelope + +The parent's new envelope supersedes the old direct-loop/1 GiB proposal: one +individually exact ignored test per disposable process, one libtest test thread, +one owned Worker thread and one owned Rayon pool worker from Request threads=1. +Desktop still enables Rayon; no zero-parallel or zero-worker claim is valid. +These are the intended numerical/test ownership roles, not a measured total OS +thread count. The external owner retains process-group supervision. + +The proposed limits are 512 MiB address space, 30 s wall, 20 s CPU, 1 MiB aggregate +captured log, existing per-file 1 MiB enforcement and a reviewed finite source +envelope for named application paths, followed by post-exit inventory. TERM then +owned process-group KILL after 1 s remains supervisor-owned. Do not infer peak +resource use from limits. If an already supported supervisor control is absent, +report that exact prerequisite; do not invent new security/provisioning work. + +This explicitly replaces the earlier review's aggregate-filesystem-enforcement +prerequisite with the parent's qualified finite-source design. Native named paths +are four manifests, at most one serial staging link, one database and its DELETE +journal (seven); material has database/journal (two). SQLite temp_store=FILE can +create additional transient stores, including anonymous/unlinked files. Their +count/peak is not statically sealed. The visible success inventory <=8 files/8 MiB +is an observation/assertion, not a hard aggregate or historical peak cap. Existing +per-file/process/time controls are the disclosed proportionate proposal to the +execution owner; no new aggregate storage mechanism is required. Unexpected +output/bound failure, timeout or numerical failure preserves disposition with no +tuning/retry/fallback. Limits do not become measured consumption. + +## Fixture and ownership boundaries + +Native remains one track, eight cells, one requested interval, at most 100 pressure +iterations, no source/emitter/model/provider/configured owner. Its explicit saved +v2 spec controls density, tolerances, memory and fixed dt; no normal-preview +defaults may replace those values. The worker returns before outcome/readback +assertions. A shortened fixed dt refuses normally and preserves its archive. + +Material remains 64 initially stationary particles, one public step, at most 16 +internal preparations, no outer retry or pressure-solver workload. Verify the +constructor/fill and explicit config against the final helper source before +sealing. Actual accepted time/dt, rather than a literal requested endpoint, +governs preview/record/result association. Force metadata, units and physics +remain canonical; the test does not implement an alternate evaluator. + +Worker::start must receive the real validated per-track snapshot and only the +private cfg(test) temporary-gate bypass. Freeze, eligibility, budgets and exact +run/publication binding stay enforced. No App update/start, GUI, observer, +progress/failure rendezvous, collision asset, model/provider or subprocess is +needed. Keep all captured owner handles/lifetimes explicit in full source review. +The external supervisor bounds join; do not add cleanup cancellation or another +scheduler/worker after a failure. + +## Readback, rerun intent and retained artifacts + +Native can verify the finalized v2 member through the existing cold-open/pin path, +then prepare a child intent with retained spec hash/force bytes and cleared attempt +without a second numerical call. That is a prepared-intent check, not production +rerun execution or proof of rerun numerical equivalence. + +Material has no serialized material FrozenSpec/cold-rerun API in the application. +Retaining a Request clone and rebinding its force snapshot from cold recorded +metadata is an honest, narrower check. Keep that limitation explicit: it does not +reconstruct all material controls from an archive or establish a complete cold +material rerun. Do not create such a format to satisfy this test. + +Use a fresh explicit absolute supervisor-owned data root, with no temp fallback, +replacement or cleanup guard. Preserve outputs on success/error/panic/timeout. +The test inventory is only reached on success; the supervisor must still collect +post-exit file names/sizes/hashes and survivor/disposition evidence after failure. +No test Drop cleanup or automatic root deletion is permitted. Normal production +SQLite commits may unlink journals; successful manifest publication unlinks its +staging link. This is distinct from test cleanup, and those normal transient paths +cannot be claimed as retained artifacts or historical peak-storage evidence. + +## Preparation gates and documentation alignment + +Both ignored bodies now exist. The amended contract reports list-only compilation +of 201 tests including the two exact names; this reviewer did not repeat that +enumeration/build. Before execution, independently review the complete production/ +helper closure and seal final source/test/lock/toolchain/features and binary. The +older executable supplies no integration evidence. The final contract now uses +real Worker::start, 512 MiB and qualified transient-storage semantics; the earlier +documentation-alignment finding is resolved. List-only/compile are preparation, +never workload or runtime acceptance. + +Passing future cases could establish tiny real-worker dispatch/effect/custody and +cold-readback association. They do not establish live Cancel/join behavior under +interruption, native/UI interaction, mixed-member runtime, scientific accuracy, +legacy equivalence or full cold rerun. Production launch stays gated until the +separately authorized execution evidence is reviewed for its exact scope. + +## Future capability-aware gate-removal candidate review + +Re-read contract SHA-256 +`b76c5ebd188fbeb37165a56d3e1828f769e7fa695a503a6223f081a6bdef9654`. +The four intended thread roles are now explicitly distinguished from a measured/ +enforced count, and the 30-second wall timeout deadline excludes teardown latency. +These are accurate qualifications, not new resource measurements. + +The final gate-removal section defines a suitable **future candidate scope**: +main/SceneUi readiness and the smallest focused eligibility/fixture tests, with +Request validation/launch checks adjusted only where the temporary gate lives. +No solver, force evaluator, recording schema, saved-spec migration, new worker or +alternate runtime switch belongs in that change. No gate code is admitted now. +Removing a single blanket check is insufficient because visible setup readiness +and consumed Request/dispatch readiness must agree. + +The future candidate should satisfy these criteria: + +- One typed capability decision uses the effective target/workflow and immutable + consumed request intent, rather than remembered material mode, viewport display, + worker presence or status strings. Native carrier and particle-material preview + are the only force-enabled positive cases. The visible launch projects the same + result that is rechecked before Worker/output admission. +- Direct `start_configured`, Accepted Session, AI comparison, transport/provider/ + free-surface and unsupported lab routes retain explicit refusal before any + worker, model/provider startup, directory, manifest or recorder side effect. + Unsupported metadata must not disappear through an empty/disabled/zero shortcut: + distinguish None from Some(empty), and validate disabled/zero objects normally. +- Freeze valid draft intent once before launch and retain per-track custody. + A failure preserves draft/current retained history and a concise visible reason. + Positive native/material readiness does not bypass any existing geometry, budget, + numerical-policy, immutable-binding or owner eligibility validation. +- Saved native reruns resolve each stored member's exact optional snapshot, + independent of the current draft. Cover v1 None, v2 Some, explicit empty and + mixed independently valid members; no `.or(current_draft)` substitution or + historical migration. Preparation-only rerun clones remain non-executable. +- Remove the private `cfg(test)` bypass and obsolete fixture fields only with + equivalent positive/negative production eligibility coverage. No replacement + hidden flag, test-only production routing or environment escape hatch remains. +- Focused inert tests exercise each supported/unsupported readiness decision and + demonstrate no dispatch/output intent for refusal, including direct methods + rather than only a disabled button. Full helper inspection precedes any exact + test execution; do not run numerical fixtures to prove the decision table. + +Prerequisite ordering must avoid a circular native gate. Before preparing code, +the two exact runtime cases need separately admitted passing evidence, and the +currently reachable native authoring/gesture/retained presentation requirements +need exact-source acceptance. Actual force-enabled UI launch cannot precede the +candidate code that enables it; it belongs to a subsequent separately bounded +candidate-source native check, before broader feature acceptance. Native launch, +progress/Cancel/join and unsupported-route behavior are not established by the +two one-step ignored probes. No duplicate native owner/session is implied. + +**Disposition:** the identified candidate is reviewable and proportionate with +these criteria. Prerequisite runtime/native evidence is still absent from this +review; no implementation or workload is authorized by this future plan review. + +Final re-read of contract SHA-256 +`1b96cb00db82e92978282c213c4abea54ee06e382c5f994edb6185f99fd5ad71` +confirms the owner incorporated the shared typed effective-target policy, +unsupported empty/disabled/zero refusal, immutable saved intent and noncircular +native ordering. The candidate-design conditions are resolved. No remaining +plan/design blocker exists for that later bounded candidate once its stated +runtime/reachable-native prerequisites are met. No gate code is admitted now. + +## Ignored-list correction — preparation only + +The owner reports that the ignored-only enumeration contains **four** tests: +the two newly prepared integration cases plus two pre-existing ignored cases. +An owner-side list-validation assertion incorrectly expected two total entries +and failed. No test body ran; preserve that failed preparation receipt and do not +report it as a numerical/runtime failure or a passing membership check. + +Correct validation should check the two exact fully qualified integration names +are present in the ignored-only list, without asserting that the entire ignored +suite has only two entries. The other two entries do not gain execution admission. +This review did not rerun list commands. Never replace the intended exact-case +commands with a broad `--ignored` execution to resolve a listing mismatch. + +Subsequent receipt read: `/tmp/rheon-ui-force-worker-enumeration-check.log` +confirms both requested fully qualified names are ignored, four ignored cases +exist in total, and the earlier expected-total-two assertion was an enumeration +mistake with no body execution. This reviewer read that corrected receipt without +repeating commands. The owner additionally reports 201 total tests and exactly +one occurrence of each new name in both lists; those raw-list counts were not +independently recomputed by this review. The corrected receipt resolves the +reported listing mismatch and supplies no runtime acceptance. diff --git a/docs/plans/ui-refinement/reports/runtime-force-worker-test-source-review.md b/docs/plans/ui-refinement/reports/runtime-force-worker-test-source-review.md new file mode 100644 index 00000000..b193506c --- /dev/null +++ b/docs/plans/ui-refinement/reports/runtime-force-worker-test-source-review.md @@ -0,0 +1,61 @@ +# Independent ignored Worker integration test source review + +## Disposition and authority + +Reviewed preparation only in `/workspace/Rheon-ui-authoring`, HEAD `1b377742e2dd2df0cc90c99c3ad735bfd480caad`. The two numerical/SQLite bodies remain ignored and are not admitted for execution by this review. The parent authorized source preparation, complete helper review and compile/list gates; no numerical run, production gate removal, native session, second worker/rerun or filesystem fixture execution is authorized here. + +The reviewer read the complete new module and its relevant production/core helper closure and wrote only this report. No build, test, binary, numerical/model/evaluator workload, SQLite open, artifact creation/cleanup or native action was executed by the reviewer. Suitable to seal as an ignored, not-executed preparation checkpoint, with the resource/evidence limits below. No material source-body blocker remains for preparation; this is not execution admission. + +## Complete execution path, if separately authorized later + +Both ignored tests construct a literal version-1 scene with one enabled Global Directional acceleration `[0.25,0,0]` m/s², id 1, identity quaternion, anchor `[0.5;3]` m and activation from zero without an end. Freeze and canonical metadata encoding preserve the immutable document; no alternate force evaluator appears in the test. + +`output_root` requires `RHEON_UI_FORCE_TEST_ROOT` to be an explicit absolute canonical existing nonsymlink directory, with no fallback, and checks it is empty. TMPDIR and SQLITE_TMPDIR must equal it. Supervisor log/receipt/seal destinations therefore must be separate. The source checks do not establish exclusive ownership or protect against concurrent path replacement; the execution owner must supply the already admitted exclusive fresh root. Existing runtime publication may create directories and staging files inside that root. There is no test Drop guard, cancellation cleanup, remove_dir or artifact deletion. + +`request` enables only the private cfg(test) temporary force gate bypass; every normal freeze, support, count, budget and snapshot/recording validation remains. It requests one track/step/thread, edge-2 preview, no observer/emitter/mesh/configured/provider/model/child/rendezvous. `worker` directly calls real `Worker::start`, destructures the returned handle/history/progress, joins, then checks the joined result, progress and row count. Other Worker field handles may drop but the worker owns its required Arc clones. There is no App Drop cancellation, detached join, second scheduler construction in the helper or second Worker call. The production start creates one owned Worker thread and one Rayon pool worker at threads=1; this is not a total measured OS-thread count. CpuScheduler construction itself does not spawn a service thread. + +The production path is Worker→run_owned→run_simulations, then native or material branch. It uses the existing per-track resolver for the canonical step adapter, Inspector begin/record and retained history; force encoding does not substitute a current draft. Construction, stepping, SQLite recorder/reader creation, immutable manifest publication and cold decode are real operations in these tests, not pure metadata probes. No current admission is given to invoke them. + +## Native case + +Prepared exact ignored name: `force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2`. + +A one-member v2 FrozenSpec controls eight cells, 0.5 m spacing, density 2 kg/m³, no source/emitter, one interval of 1e-6 s, JacobiPcgV1, max 100 pressure iterations and 1 MiB retained-array budget. The saved-spec branch constructs from that spec rather than the normal preview's density/tolerance defaults. Core transient work and SQL/process overhead are separate from retained-array budget. No extra numerical baseline or outer retry is added; fixed-dt saved intent still refuses a shortened step. + +The test initially publishes intent, passes it and the same frozen snapshot to Worker, joins, opens the finished immutable manifest/member, verifies the finalized pin/run association and cold reader, then decodes constructor and accepted records. It checks generation/stage/count/completed status, exact document bytes on both publications and actual recorded pressure controls. The predeclared effect guard expects the closed-box hydrostatic X pressure difference `rho*a*0.5m=0.25Pa`, with finite values and ±0.05 Pa margin. That coarse 20% tolerance distinguishes force dispatch from zero-input metadata only; it is not pressure/scientific accuracy validation or a tuned result. It additionally checks accepted fixed dt and preview/result timestamp association. + +The subsequent native Request clone prepares constructor-zero intent with the stored v2 hash/snapshot and a cleared attempt, but retains old output/receipt paths and is explicitly not executed or execution-ready. It does not test production App rerun routing or numerical rerun equivalence. A separate mutated draft is then followed by rereads of both archive records; this verifies intended immutable association if the case eventually passes, without a second numerical call. + +## Material case + +Prepared exact ignored name: `force_runtime_integration_tests::material_force_dispatch_records_immutable_scene`. + +The default water/ambient material configuration is narrowed to EmittedFluid, samples-per-axis 4, max particles 64, 1 MiB retained-array limit and gravity `[0,-1,0]` m/s². Core constructor source seeds 4³ stationary emitted-fluid particles for this full 1 m cube. Emitter and collision mesh are absent. There is one public particle step; canonical `step_fields_impl` contains at most 16 internal preparations and no external retry/pressure-solver workload is added. + +After join, the test cold-opens and verifies two records, exact force metadata and material configuration/geometry; initial and accepted particle counts are 64. It uses actual positive finite accepted dt≤requested dt, not a fabricated requested endpoint. The predeclared effect threshold is net X momentum within 50–150% of initial total mass×0.25 m/s²×actual dt. Symmetric pair forces and purely vertical gravity do not intentionally supply net X force; this conservative guard excludes merely tiny positive roundoff. Reader verification validates numeric fields and the explicit guard requires finite mass/change. It remains a coarse dispatch symptom, not scientific validation or legacy equivalence. + +The test checks accepted timing, diagnostics count, preview/result association and actual force controls. It rebinds a retained Request clone from cold accepted force metadata and checks the original typed material configuration, then mutates an independent draft and rereads both archive documents. There is no serialized material FrozenSpec/cold-rerun API here: this does not reconstruct all material controls from an archive or establish full material rerun/parity. + +## Output and failure conditions before any execution handoff + +The helper's final inventory is reached only on success, checks regular nonsymlink files, hashes them and asserts at most eight files/8 MiB with 1 MiB each. It is an outcome assertion, not enforcement or a simultaneous-file proof. The supervisor must inventory after every child exit/failure/timeout/panic and retain actual filenames/sizes/hashes, stdout/stderr, binary/source seals and survivor disposition, without automatic deletion or parameter tuning/retry/fallback. + +Source-known native names are `intent.json`, `member-0-started.json`, `member-0-finished.json`, `completed.json`, and one fresh method-labelled SQLite archive. Atomic publication uses sequential `.rheon-manifest-PID-counter.tmp` names; success removes its normal staging link, but an I/O/publication error may retain it and propagates. SQLite uses DELETE rollback journaling, not WAL; a journal may remain after failure. Material creates one fresh archive and has no run-set manifests. These are existing owner mechanics, distinct from prohibited fixture cleanup. + +SQLite `temp_store=FILE` remains enabled. Readers perform quick/integrity/foreign-key checks and indexed ordered field reads; multiple reader owners can coexist after join. The amended contract now separates the seven native/two material named application paths from SQLite transient stores, explicitly marking the latter's count/peak not statically sealed. It labels the eight-file/8 MiB guard success-only final observation, not a maximum number of simultaneously open or anonymous temporary files, disk blocks or cumulative I/O bytes. This honest limitation resolves the preparation documentation issue; it does not supply an aggregate storage bound or execution assurance. Existing per-file/process/time controls remain proposed for the trusted fixture, with that gap disclosed to the execution owner. This review does not demand new mounts/cgroups/provisioning for preparation or invent a guarantee from the source-known names. + +The proposed future envelope is one exact ignored case per supervised disposable process, 30 s wall/20 s CPU/512 MiB address space/1 MiB captured log, existing per-file 1 MiB limits, TERM→owned-group KILL after 1 s, finite source/file accounting and retained post-exit inventory. These are proposed limits, not measured use or an execution grant. No runtime supervisor source has been reviewed for these cases yet. The earlier 10 s pure-UI wrapper is not automatically admitted for numerical/SQLite use. + +At initial review `runtime-force-validation-contract.md` still described bodies absent, no Worker::start, direct-loop execution and 1 GiB/aggregate-root semantics. The amendment replaces them with the actual ignored-module/Worker path, 512 MiB proposal, separate capture/data roots, finite named paths and the unsealed SQLite transient limitation, honest material/prepared-rerun scope and 201-name enumeration. The plan review makes preparation-only authority clear. Intended main/libtest/Worker/Rayon roles must not be represented as measured or hard-limited total OS threads. A 30 s timeout followed by TERM/KILL/reap is a deadline plus teardown, not proof of a hard wall-clock process-disappearance guarantee; final supervisor disposition must remain explicit. + +## Preparation source and compile receipt + +Final inspected test-module SHA-256: `src/bin/desktop/force_runtime_integration_tests.rs` = `da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584`. Main's only change is the cfg(test) module declaration; `src/bin/rheon-desktop.rs` = `6e70c7957357611fa4c0610dddd4f1673544882821b9dab47b481dc2c394eff4`. Both bodies retain explicit `#[ignore = "numerical/SQLite worker integration; separate bounded execution admission required"]`. No production launch behavior changes. + +Read-only receipt: `/tmp/rheon-ui-force-worker-list2.log` records test compilation success in 13.86 s and lists 201 tests/zero benchmarks, including the two exact names above. Source attributes establish they are ignored; enumeration is not execution. The earlier list1 compilation failed because SnapshotStage was imported through a private recording path; the final source uses its public root re-export. That failure is preserved and superseded by list2. No numerical/SQLite invocation or result log exists as part of this reviewed preparation. Locked compile/static/final binary/source transfer seals beyond this receipt remain owner gates, not inferred successes. + +## Remaining gates + +Compile/list-only may establish that both exact tests exist and are ignored, without invoking either body. Seal final source/test bytes, Cargo.lock, toolchain/features, exact test executable and actual commit/tree; the older pure/native executable is not this future test binary. Independently review the amended execution contract/supervisor/file proof and obtain the separate execution authority before running either exact ignored name. Production launch remains gated. + +Even future passing cases would not establish native/UI usability, live Cancel/join interruption, mixed-member runtime, full cold rerun, legacy equivalence, field accuracy or broad scientific validation. Preserve the prior focus/timeline/native evidence and its limitations separately. diff --git a/src/bin/desktop/force_runtime_integration_tests.rs b/src/bin/desktop/force_runtime_integration_tests.rs new file mode 100644 index 00000000..70581eb8 --- /dev/null +++ b/src/bin/desktop/force_runtime_integration_tests.rs @@ -0,0 +1,370 @@ +//! Prepared numerical/SQLite probes. Ignored; never execute without separate +//! bounded admission. No cleanup guard: all artifacts survive success or panic. +use super::*; +use rheon::recording::{RecordedCarrier, RecordedMaterial, RecordingReader}; +use rheon::{ + ForceKind, ForceObject, ForceScene, ForceSnapshot, ForceSupport, ForceTime, ForceTransform, + ForceUnits, SceneObjectId, SnapshotStage, +}; +use std::path::{Path, PathBuf}; + +fn fields() -> ForceSnapshot { + ForceScene { + version: 1, + objects: vec![ForceObject { + id: SceneObjectId(1), + name: "Desktop custody probe".into(), + enabled: true, + transform: ForceTransform { + translation: [0.5; 3], + ..Default::default() + }, + units: ForceUnits::Acceleration, + kind: ForceKind::Directional { + vector: [0.25, 0.0, 0.0], + }, + support: ForceSupport::Global, + time: ForceTime::default(), + }], + } + .freeze() + .unwrap() +} +fn bytes(fields: &ForceSnapshot) -> Vec { + rheon::recording::encode_force_metadata(fields).unwrap() +} +fn output_root() -> PathBuf { + let root = PathBuf::from( + std::env::var_os("RHEON_UI_FORCE_TEST_ROOT") + .expect("supervisor must supply one fresh exclusive root; no temp fallback"), + ); + assert!(root.is_absolute()); + let metadata = std::fs::symlink_metadata(&root).unwrap(); + assert!(metadata.is_dir() && !metadata.file_type().is_symlink()); + assert_eq!(root.canonicalize().unwrap(), root); + // SQLite temp_store is FILE in the canonical owner. Keep any transient + // files inside this same disposable data root, distinct from supervisor logs. + for key in ["TMPDIR", "SQLITE_TMPDIR"] { + assert_eq!(std::env::var_os(key), Some(root.as_os_str().to_os_string())); + } + assert!( + std::fs::read_dir(&root).unwrap().next().is_none(), + "root must be fresh" + ); + eprintln!("retained artifact root: {}", root.display()); + root +} +fn request(root: &Path, fields: &ForceSnapshot) -> Request { + Request { + force_fields: vec![Some(fields.clone())], + force_validation: true, + collision_mesh: None, + size: 2, + steps: 1, + tight: false, + threads: 1, + methods: vec![PressureImplementation::JacobiPcgV1], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Edge(2), + domain_side: 1.0, + requested_dt: 1e-6, + material: None, + material_color: [0; 3], + recording_limits: rheon::recording::RecordingLimits { + max_database_bytes: 1024 * 1024, + max_record_bytes: 65536, + max_records: 2, + }, + recording_directory: root.to_str().unwrap().into(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + } +} +fn worker(request: Request) -> (Arc, Vec) { + // Join before any outcome assertions. No App::Drop, cancellation, second + // scheduler or detached work owner. External supervisor bounds the wait. + let Worker { + history, + progress, + handle, + .. + } = Worker::start(request).unwrap(); + let joined = handle.join(); + let result = joined.expect("worker panic; artifacts retained"); + let rows = result.expect("worker error; artifacts retained"); + assert_eq!(progress.load(Ordering::Relaxed), 1); + assert_eq!(rows.len(), 1); + (history, rows) +} +fn readback(path: &Path, intended: &Request) -> RecordingReader { + let reader = RecordingReader::open(path, intended.recording_limits).unwrap(); + reader.verify().unwrap(); + assert_eq!(reader.count().unwrap(), 2); + let status = reader.status().unwrap(); + assert_eq!(status.outcome, "completed"); + assert_eq!(status.accepted_generation, Some(1)); + let fields = intended.fields_for_track(0).unwrap(); + inspector::verify_force_fields(reader.run(), Some(fields)).unwrap(); + for ordinal in 0..2 { + let step = reader.select(ordinal).unwrap(); + assert_eq!(step.generation, ordinal as u64); + assert_eq!( + step.stage, + if ordinal == 0 { + SnapshotStage::Constructor + } else { + SnapshotStage::Accepted + } + ); + let actual = rheon::recording::recorded_force_fields(&step) + .unwrap() + .unwrap(); + assert_eq!(bytes(&actual), bytes(fields)); + } + reader +} +fn inventory(root: &Path) { + use sha2::{Digest, Sha256}; + let mut total = 0_u64; + let mut files = 0; + for entry in std::fs::read_dir(root).unwrap() { + let path = entry.unwrap().path(); + let metadata = std::fs::symlink_metadata(&path).unwrap(); + assert!(metadata.is_file() && !metadata.file_type().is_symlink()); + assert!(metadata.len() <= 1024 * 1024); + total += metadata.len(); + files += 1; + let data = std::fs::read(&path).unwrap(); + eprintln!( + "artifact={} bytes={} sha256={:x}", + path.display(), + data.len(), + Sha256::digest(data) + ); + } + assert!(files <= 8 && total <= 8 * 1024 * 1024); + eprintln!("retained files={files} bytes={total}; no cleanup performed"); +} +#[test] +#[ignore = "numerical/SQLite worker integration; separate bounded execution admission required"] +fn native_force_dispatch_records_and_cold_binds_v2() { + let root = output_root(); + let fields = fields(); + let mut request = request(&root, &fields); + let native = run_sets::NativeCarrierSpec { + counts_xyz: [2; 3], + spacing_m: [0.5; 3], + origin_m: [0.0; 3], + density_kg_m3: 2.0, + pressure_method: run_sets::NativePressureMethod::JacobiPcgV1, + tolerances: run_sets::NativeTolerances { + relative_residual: 1e-11, + absolute_residual_m3_s2: 1e-12, + predicted_divergence_per_s: 1e-9, + actual_divergence_per_s: 1e-6, + max_courant: 1.0, + max_pressure_iterations: 100, + }, + requested_dt_s: 1e-6, + requested_steps: 1, + source: None, + simulation_memory_limit_bytes: 1024 * 1024, + recording: run_sets::ArchiveBudget { + max_database_bytes: 1024 * 1024, + max_record_bytes: 65536, + max_records: 2, + }, + }; + let frozen = run_sets::FrozenSpec::new(native) + .unwrap() + .with_force_fields(&fields) + .unwrap(); + let intent = run_sets::RunSetRevision { + schema_version: 1, + set_id: "desktop-force-probe".into(), + parent_sha256: None, + baseline_member: "native-0".into(), + members: vec![run_sets::RunSetMember { + member_id: "native-0".into(), + spec: frozen.clone(), + differences_from_baseline: vec![], + attempt: None, + }], + }; + intent.save_new(&root.join("intent.json")).unwrap(); + request.saved_set = Some(intent); + request.manifest_receipt = Some(Arc::new(std::sync::Mutex::new(root.join("intent.json")))); + let retained = request.clone(); + let (history, rows) = worker(request); + let finished = run_sets::RunSetRevision::open(&root.join("member-0-finished.json")).unwrap(); + let member = &finished.members[0]; + assert_eq!(member.spec.sha256, frozen.sha256); + let run_sets::MemberState::Ready(cold) = run_sets::open_member(&root, member) else { + panic!("cold immutable binding failed; preserve all artifacts") + }; + let reader = readback( + &root.join(&member.attempt.as_ref().unwrap().archive_relative_path), + &retained, + ); + assert_eq!(cold.run(), reader.run()); + let initial = RecordedCarrier::decode(reader.run(), &reader.select(0).unwrap()).unwrap(); + let raw = reader.select(1).unwrap(); + let accepted = RecordedCarrier::decode(reader.run(), &raw).unwrap(); + assert!(initial.interval_pressure.is_none()); + assert!( + initial + .x + .iter() + .chain(&initial.y) + .chain(&initial.z) + .all(|v| *v == 0.0) + ); + let (start, pressure) = accepted.interval_pressure.as_ref().unwrap(); + assert_eq!(*start, 0.0); + // A uniform X acceleration in this closed box produces a nonzero interval + // pressure gradient. Metadata alone cannot satisfy this dispatch-effect guard. + assert_eq!(pressure.len(), 8); + for (index, pressure) in pressure.iter().enumerate() { + // Hydrostatic dp/dx=rho*a:2kg/m³×0.25m/s²×0.5m=0.25Pa. + // Predeclared coarse20% guard distinguishes an applied field from zero; + // it is not a pressure-accuracy validation or tuned after execution. + let expected = (index % 2) as f64 * 0.25; + assert!(pressure.is_finite() && (pressure - expected).abs() <= 0.05); + } + assert!(raw.controls.contains("external-force-metadata-v1")); + assert_eq!(accepted.dt, Some(retained.requested_dt)); + let (count, preview) = history.view(0, 1, false).unwrap(); + assert_eq!(count, 2); + assert_eq!(preview.unwrap().time.to_bits(), accepted.time.to_bits()); + assert_eq!(rows[0].time.to_bits(), accepted.time.to_bits()); + // Prepare constructor-zero rerun intent from the cold v2 member, without + // executing a second step/worker or consulting the mutable authoring draft. + // It still has old output/receipt paths: this is NOT an executable rerun. A + // production rerun must allocate fresh paths through the App owner. + let mut rerun = retained.clone(); + let mut next = finished.clone(); + next.parent_sha256 = Some(finished.sha256().unwrap()); + next.set_id = "prepared-rerun".into(); + next.members[0].attempt = None; + rerun.force_fields = vec![next.members[0].spec.force_snapshot().unwrap()]; + rerun.saved_set = Some(next); + validate_request(&rerun).unwrap(); + assert_eq!(bytes(rerun.fields_for_track(0).unwrap()), bytes(&fields)); + let mut changed_draft = fields.scene().clone(); + changed_draft.objects[0].name = "Later draft".into(); + assert_ne!( + bytes(&changed_draft.freeze().unwrap()), + bytes(rerun.fields_for_track(0).unwrap()) + ); + // Re-read both archive records AFTER mutating a separate authoring draft. + for ordinal in 0..2 { + let step = reader.select(ordinal).unwrap(); + assert_eq!( + bytes( + &rheon::recording::recorded_force_fields(&step) + .unwrap() + .unwrap() + ), + bytes(&fields) + ); + } + inventory(&root); +} +#[test] +#[ignore = "numerical/SQLite worker integration; separate bounded execution admission required"] +fn material_force_dispatch_records_immutable_scene() { + let root = output_root(); + let fields = fields(); + let mut request = request(&root, &fields); + request.material = Some(rheon::ParticleFluidConfig { + fill: rheon::DomainFill::EmittedFluid, + samples_per_axis: 4, + max_particles: 64, + memory_limit: 1024 * 1024, + gravity: [0.0, -1.0, 0.0], + ..Default::default() + }); + let retained = request.clone(); + let (history, rows) = worker(request); + let path = history.inspector.archive_path().unwrap(); + let reader = readback(&path, &retained); + assert!( + reader + .run() + .configuration + .starts_with(&format!("{:?}; collision=", retained.material.unwrap())) + ); + assert_eq!(reader.run().counts, [2; 3]); + assert_eq!(reader.run().spacing, [0.5; 3]); + assert_eq!(reader.run().origin, [0.0; 3]); + let initial = RecordedMaterial::decode(&reader.select(0).unwrap()).unwrap(); + let raw = reader.select(1).unwrap(); + let accepted = RecordedMaterial::decode(&raw).unwrap(); + assert_eq!(initial.particles.len(), 64); + assert_eq!(accepted.particles.len(), 64); + assert!(initial.particles.iter().all(|p| p.velocity == [0.0; 3])); + let initial_momentum: f64 = initial + .particles + .iter() + .map(|p| p.mass * p.velocity[0]) + .sum(); + let momentum: f64 = accepted + .particles + .iter() + .map(|p| p.mass * p.velocity[0]) + .sum(); + // Symmetric pair forces and vertical gravity cannot supply positive net X + // momentum here. A document-only path does not satisfy this effect guard. + let dt = accepted.dt.unwrap(); + assert!(dt.is_finite() && dt > 0.0 && dt <= retained.requested_dt); + let mass: f64 = initial.particles.iter().map(|p| p.mass).sum(); + let expected_momentum = mass * 0.25 * dt; + let change = momentum - initial_momentum; + // Predeclared50% coarse guard excludes tiny positive pair-force roundoff. + assert!(mass.is_finite() && mass > 0.0 && change.is_finite()); + assert!((0.5 * expected_momentum..=1.5 * expected_momentum).contains(&change)); + assert!(raw.controls.contains("external-force-metadata-v1")); + assert_eq!(accepted.time.to_bits(), dt.to_bits()); + let (_, diagnostics) = accepted.diagnostics.as_ref().unwrap(); + assert_eq!(diagnostics.len(), 64); + let (count, preview) = history.view(0, 1, false).unwrap(); + assert_eq!(count, 2); + assert_eq!(preview.unwrap().time.to_bits(), accepted.time.to_bits()); + assert_eq!(rows[0].particle_report.unwrap().dt.to_bits(), dt.to_bits()); + // Existing material workflows retain Request in memory, not a serialized + // material FrozenSpec. Check that retained typed intent can rebind the cold + // recorded scene; this does NOT claim a production cold material-rerun API. + let mut rerun = retained.clone(); + rerun.force_fields = vec![accepted.force_fields.clone()]; + validate_request(&rerun).unwrap(); + assert_eq!( + format!("{:?}", rerun.material), + format!("{:?}", retained.material) + ); + assert_eq!(bytes(rerun.fields_for_track(0).unwrap()), bytes(&fields)); + let mut changed_draft = fields.scene().clone(); + changed_draft.objects[0].enabled = false; + assert_ne!( + bytes(&changed_draft.freeze().unwrap()), + bytes(rerun.fields_for_track(0).unwrap()) + ); + // Re-read both archive records AFTER mutating a separate authoring draft. + for ordinal in 0..2 { + let step = reader.select(ordinal).unwrap(); + assert_eq!( + bytes( + &rheon::recording::recorded_force_fields(&step) + .unwrap() + .unwrap() + ), + bytes(&fields) + ); + } + inventory(&root); +} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index ed77824e..bcecf736 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -53,6 +53,9 @@ pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; #[cfg(test)] +#[path = "desktop/force_runtime_integration_tests.rs"] +mod force_runtime_integration_tests; +#[cfg(test)] #[path = "desktop/force_runtime_ui_tests.rs"] mod force_runtime_ui_tests; #[cfg(test)] From e5e6cff984d7f220b2b3a741fb8759aa542fccc3 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:47:17 +0000 Subject: [PATCH 18/25] docs(ui): preserve bounded force worker validation receipts --- .../manifest.json | 62 +++++++++ .../material/combined.log | 9 ++ .../material/disposition.json | 70 ++++++++++ .../material/preflight-seals.json | 28 ++++ .../material/supervisor.py | 126 ++++++++++++++++++ .../native/combined.log | 13 ++ .../native/disposition.json | 94 +++++++++++++ .../native/preflight-seals.json | 28 ++++ .../native/supervisor.py | 126 ++++++++++++++++++ .../post-exit-artifact-index.json | 81 +++++++++++ .../supervisor-review.md | 15 +++ docs/plans/ui-refinement/execution-ledger.md | 28 ++++ docs/plans/ui-refinement/plan.md | 20 +-- .../force-worker-runtime-results-review.md | 52 ++++++++ .../runtime-force-validation-contract.md | 10 +- 15 files changed, 753 insertions(+), 9 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/combined.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/disposition.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/preflight-seals.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/supervisor.py create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/combined.log create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/disposition.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/preflight-seals.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/supervisor.py create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/post-exit-artifact-index.json create mode 100644 docs/plans/ui-refinement/evidence/force-worker-execution-20261021/supervisor-review.md create mode 100644 docs/plans/ui-refinement/reports/force-worker-runtime-results-review.md diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/manifest.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/manifest.json new file mode 100644 index 00000000..a9639088 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/manifest.json @@ -0,0 +1,62 @@ +{ + "execution_source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "execution_tree": "4cdf4b221bb47704acfcda6b682453ed60344908", + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "binary_preserved": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/sealed-runtime-tests-3b3fcce.bin", + "raw_data_preserved": "/workspace/scratch/rheon-ui-force-worker-execution-20261021", + "production_gate": "closed", + "peak_memory": "not measured", + "temporary_file_peak": "unknown", + "files": [ + { + "file": "material/combined.log", + "bytes": 567, + "sha256": "822087d8721d2a3da1fe4bb09de5346931338c9011cb0ff291bf1a9609f33d3d" + }, + { + "file": "material/disposition.json", + "bytes": 2469, + "sha256": "4f06fc37a1d1c07b89e6dfe2e674a8e7b098f29ca17081f8bd0f3c7f7c5ef819" + }, + { + "file": "material/preflight-seals.json", + "bytes": 1093, + "sha256": "708130c2b29d3752caaf9c1684565b430c3d144d4489b4f111a1cf01cf8c8ebc" + }, + { + "file": "material/supervisor.py", + "bytes": 7733, + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + }, + { + "file": "native/combined.log", + "bytes": 1296, + "sha256": "b8effeccefde02cb06b78d4dd1c10814c2e6f05ae778654db8b715557873b00d" + }, + { + "file": "native/disposition.json", + "bytes": 3153, + "sha256": "9bb83d4450934309e912903f6d3a9b0c0493007dcd518cd8f717d732eec2bbb1" + }, + { + "file": "native/preflight-seals.json", + "bytes": 1087, + "sha256": "9522942fc315793a41099554858a400d81d7fef658be01a733631f0912656465" + }, + { + "file": "native/supervisor.py", + "bytes": 7733, + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + }, + { + "file": "post-exit-artifact-index.json", + "bytes": 2638, + "sha256": "4b9dc8a8188671cb13ce67a1585a589f012ae8cf7e92a7918b067d59caea8a85" + }, + { + "file": "supervisor-review.md", + "bytes": 4097, + "sha256": "555442228d3fe9f60916a7de771b73fff9088a2a38e58107656856d48dfbfdd6" + } + ] +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/combined.log b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/combined.log new file mode 100644 index 00000000..be022aba --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/combined.log @@ -0,0 +1,9 @@ + +running 1 test +test force_runtime_integration_tests::material_force_dispatch_records_immutable_scene ... retained artifact root: /workspace/scratch/rheon-ui-force-worker-execution-20261021/material/DATA +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/material/DATA/material-1792543046180951263-59778-0.sqlite bytes=49152 sha256=17ace71c0ed82ea905b11b90fdb1ff0413afddb5f700ad4a84a249abb26c14d5 +retained files=1 bytes=49152; no cleanup performed +ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 200 filtered out; finished in 0.02s + diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/disposition.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/disposition.json new file mode 100644 index 00000000..0244f5d3 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/disposition.json @@ -0,0 +1,70 @@ +{ + "case": "material", + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "force_runtime_integration_tests::material_force_dispatch_records_immutable_scene", + "--exact", + "--ignored", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_timeout_seconds": 30, + "cpu_seconds": 20, + "address_space_bytes": 536870912, + "per_file_and_log_bytes": 1048576, + "term_grace_seconds": 1, + "core_bytes": 0 + }, + "source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "data": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/material/DATA", + "capture": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/material/capture", + "test_body_invocation_attempted": true, + "actions": [], + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "binary_bytes": 480456920, + "source_manifest_sha256": "603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc", + "supervisor_sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444", + "pid": 59778, + "elapsed_seconds_including_teardown": 0.032914473998971516, + "exit_code": 0, + "group_present_after_wait": false, + "observed_child_cpu_seconds": { + "user": 0.010936999999999999, + "system": 0.009525 + }, + "individual_peak_memory": "not measured; address-space limit is not a measurement", + "captured_bytes": 567, + "log_sha256": "822087d8721d2a3da1fe4bb09de5346931338c9011cb0ff291bf1a9609f33d3d", + "result": "passed-one-exact-case", + "child_pid_present": false, + "data_inventory": [ + { + "path": "material-1792543046180951263-59778-0.sqlite", + "bytes": 49152, + "kind": "file", + "sha256": "17ace71c0ed82ea905b11b90fdb1ff0413afddb5f700ad4a84a249abb26c14d5" + } + ], + "capture_inventory_before_receipt": [ + { + "path": "combined.log", + "bytes": 567, + "kind": "file", + "sha256": "822087d8721d2a3da1fe4bb09de5346931338c9011cb0ff291bf1a9609f33d3d" + }, + { + "path": "preflight-seals.json", + "bytes": 1093, + "kind": "file", + "sha256": "708130c2b29d3752caaf9c1684565b430c3d144d4489b4f111a1cf01cf8c8ebc" + }, + { + "path": "supervisor.py", + "bytes": 7733, + "kind": "file", + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + } + ], + "temporary_file_peak": "unknown; final inventory is not aggregate or anonymous peak enforcement" +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/preflight-seals.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/preflight-seals.json new file mode 100644 index 00000000..61a4b778 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/preflight-seals.json @@ -0,0 +1,28 @@ +{ + "case": "material", + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "force_runtime_integration_tests::material_force_dispatch_records_immutable_scene", + "--exact", + "--ignored", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_timeout_seconds": 30, + "cpu_seconds": 20, + "address_space_bytes": 536870912, + "per_file_and_log_bytes": 1048576, + "term_grace_seconds": 1, + "core_bytes": 0 + }, + "source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "data": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/material/DATA", + "capture": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/material/capture", + "test_body_invocation_attempted": false, + "actions": [], + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "binary_bytes": 480456920, + "source_manifest_sha256": "603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc", + "supervisor_sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/supervisor.py b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/supervisor.py new file mode 100644 index 00000000..0fd7745c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/material/supervisor.py @@ -0,0 +1,126 @@ +"""One admitted exact Worker/SQLite case. Preserve every remaining artifact.""" +import hashlib, json, os, re, resource, signal, subprocess, sys, time +from pathlib import Path +REPO=Path('/workspace/Rheon-ui-authoring') +BINARY=Path('/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f') +SEAL='af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5' +REV='3b3fccea3657d711897130643a478adb48b5edd0' +NAMES={ +'native':'force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2', +'material':'force_runtime_integration_tests::material_force_dispatch_records_immutable_scene', +} +def digest(path): + with path.open('rb') as file: return hashlib.file_digest(file,'sha256').hexdigest() +def inventory(root): + result=[] + for path in sorted(root.rglob('*')): + meta=path.lstat() + item={'path':str(path.relative_to(root)),'bytes':meta.st_size} + if path.is_symlink(): item['kind']='symlink' + elif path.is_dir(): item['kind']='directory' + elif path.is_file(): item.update(kind='file',sha256=digest(path)) + else: item['kind']='other' + result.append(item) + return result +case=sys.argv[1] +assert case in NAMES +root=Path('/workspace/scratch/rheon-ui-force-worker-execution-20261021') +root.mkdir(exist_ok=True) +case_root=root/case +case_root.mkdir(exist_ok=False) # refuses duplicate execution, including prior failures +DATA=case_root/'DATA';CAPTURE=case_root/'capture' +DATA.mkdir();CAPTURE.mkdir() +assert DATA.resolve()==DATA and CAPTURE.resolve()==CAPTURE +log=CAPTURE/'combined.log';receipt=CAPTURE/'disposition.json' +command=[str(BINARY),NAMES[case],'--exact','--ignored','--test-threads=1','--nocapture'] +limits={'wall_timeout_seconds':30,'cpu_seconds':20,'address_space_bytes':536870912,'per_file_and_log_bytes':1048576,'term_grace_seconds':1,'core_bytes':0} +state={'case':case,'command':command,'limits':limits,'source_commit':REV,'data':str(DATA),'capture':str(CAPTURE),'test_body_invocation_attempted':False,'actions':[]} +child=None +start=None +failure=None +try: + assert subprocess.check_output(['git','rev-parse','HEAD'],cwd=REPO,text=True).strip()==REV + assert not subprocess.check_output(['git','status','--porcelain'],cwd=REPO,text=True) + assert digest(BINARY)==SEAL + source_manifest=REPO/'docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json' + for item in json.loads(source_manifest.read_text()): + assert digest(REPO/item['file'])==item['sha256'],item['file'] + listed=(REPO/'docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log').read_text().splitlines() + assert listed.count(NAMES[case]+': test')==1 + state.update(binary_sha256=SEAL,binary_bytes=BINARY.stat().st_size,source_manifest_sha256=digest(source_manifest),supervisor_sha256=digest(Path(__file__))) + (CAPTURE/'preflight-seals.json').write_text(json.dumps(state,indent=2)+'\n') + (CAPTURE/'supervisor.py').write_bytes(Path(__file__).read_bytes()) + env=dict(os.environ,RHEON_UI_FORCE_TEST_ROOT=str(DATA),TMPDIR=str(DATA),SQLITE_TMPDIR=str(DATA)) + def bound(): + for key,value in [(resource.RLIMIT_CPU,20),(resource.RLIMIT_AS,536870912),(resource.RLIMIT_FSIZE,1048576),(resource.RLIMIT_CORE,0)]: + resource.setrlimit(key,(value,value)) + assert resource.getrlimit(key)==(value,value) + def group_signal(sig): + # An unreaped Popen child reserves its PID; never signal after reaping. + assert child.returncode is None + try: os.killpg(child.pid,sig) + except ProcessLookupError: pass + usage_before=resource.getrusage(resource.RUSAGE_CHILDREN) + with log.open('xb') as output: + start=time.monotonic() + state['test_body_invocation_attempted']=True + child=subprocess.Popen(command,cwd=REPO,env=env,stdout=output,stderr=subprocess.STDOUT,start_new_session=True,preexec_fn=bound) + state['pid']=child.pid + try: child.wait(timeout=max(0.0,start+30-time.monotonic())) + except subprocess.TimeoutExpired: + state['timed_out']=True;state['actions'].append('TERM owned child group');group_signal(signal.SIGTERM) + try: child.wait(timeout=1) + except subprocess.TimeoutExpired: + state['actions'].append('KILL owned child group');group_signal(signal.SIGKILL) + try: child.wait(timeout=2) + except subprocess.TimeoutExpired: state['unreaped']=True + state['elapsed_seconds_including_teardown']=time.monotonic()-start + state['exit_code']=child.poll() + try: + os.killpg(child.pid,0) + state['group_present_after_wait']=True + state['post_reap_group_probe']='unexpected group ID present; ownership unproven, not signaled' + except ProcessLookupError: state['group_present_after_wait']=False + usage_after=resource.getrusage(resource.RUSAGE_CHILDREN) + state['observed_child_cpu_seconds']={'user':usage_after.ru_utime-usage_before.ru_utime,'system':usage_after.ru_stime-usage_before.ru_stime} + state['individual_peak_memory']='not measured; address-space limit is not a measurement' + data=log.read_bytes() + state.update(captured_bytes=len(data),log_sha256=digest(log)) + assert not state.get('timed_out',False) and not state.get('unreaped',False) + assert state['exit_code']==0 and not state['group_present_after_wait'] + assert b'running 1 test' in data and NAMES[case].encode() in data + assert b'1 passed; 0 failed; 0 ignored;' in data + items=inventory(DATA) + assert all(item['kind']=='file' for item in items) + assert len(items)<=8 and sum(item['bytes'] for item in items)<=8*1024*1024 + assert all(item['bytes']<=1048576 for item in items) + db=[item for item in items if re.fullmatch(('jacobi-pcg-v1' if case=='native' else 'material')+r'-[0-9]+-[0-9]+-[0-9]+\.sqlite',item['path'])] + assert len(db)==1 + expected={db[0]['path']} | ({'intent.json','member-0-started.json','member-0-finished.json','completed.json'} if case=='native' else set()) + assert {item['path'] for item in items}==expected + state['result']='passed-one-exact-case' +except BaseException as error: + failure=repr(error);state['result']='failed-or-uncertain';state['failure']=failure +finally: + if child is not None and child.poll() is None: + state['actions'].append('TERM still-running owned child group in final disposition') + try: os.killpg(child.pid,signal.SIGTERM) + except ProcessLookupError: pass + try: child.wait(timeout=1) + except subprocess.TimeoutExpired: + state['actions'].append('KILL still-running owned child group after grace') + try: os.killpg(child.pid,signal.SIGKILL) + except ProcessLookupError: pass + try: child.wait(timeout=2) + except subprocess.TimeoutExpired: state['unreaped']=True + state['child_pid_present']=child is not None and Path('/proc',str(child.pid)).exists() + if state['child_pid_present'] or state.get('unreaped',False): + state['result']='failed-or-uncertain' + state['final_process_identity_note']='PID path remains or direct child unreaped; no post-reap signaling' + for label,path in [('data_inventory',DATA),('capture_inventory_before_receipt',CAPTURE)]: + try: state[label]=inventory(path) + except BaseException as error: state[label+'_error']=repr(error);state['result']='failed-or-uncertain' + state['temporary_file_peak']='unknown; final inventory is not aggregate or anonymous peak enforcement' + receipt.write_text(json.dumps(state,indent=2)+'\n') + print(json.dumps(state,indent=2)) +assert state['result']=='passed-one-exact-case' and not state.get('child_pid_present',False) diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/combined.log b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/combined.log new file mode 100644 index 00000000..ec6ecb3d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/combined.log @@ -0,0 +1,13 @@ + +running 1 test +test force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2 ... retained artifact root: /workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA/completed.json bytes=1883 sha256=265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA/jacobi-pcg-v1-1792543035724545053-59751-0.sqlite bytes=32768 sha256=ea8ccde4ec9a94a218443dc2570212f37cd7439c6af73e56d934f73fd4d13279 +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA/intent.json bytes=1302 sha256=71e5d32cd24ed95b5e5ee16caeceddef7c7786e4839ac1bb16a0a75d2720e946 +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA/member-0-finished.json bytes=1883 sha256=265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d +artifact=/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA/member-0-started.json bytes=1691 sha256=29068e832fcdafa3a61e1ac2dd809b1b36fa5515acaf32aaa2ab76a94b94b25e +retained files=5 bytes=39527; no cleanup performed +ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 200 filtered out; finished in 0.03s + diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/disposition.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/disposition.json new file mode 100644 index 00000000..0f2efa7a --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/disposition.json @@ -0,0 +1,94 @@ +{ + "case": "native", + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2", + "--exact", + "--ignored", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_timeout_seconds": 30, + "cpu_seconds": 20, + "address_space_bytes": 536870912, + "per_file_and_log_bytes": 1048576, + "term_grace_seconds": 1, + "core_bytes": 0 + }, + "source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "data": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA", + "capture": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/capture", + "test_body_invocation_attempted": true, + "actions": [], + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "binary_bytes": 480456920, + "source_manifest_sha256": "603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc", + "supervisor_sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444", + "pid": 59751, + "elapsed_seconds_including_teardown": 0.06492926300052204, + "exit_code": 0, + "group_present_after_wait": false, + "observed_child_cpu_seconds": { + "user": 0.020911, + "system": 0.007682 + }, + "individual_peak_memory": "not measured; address-space limit is not a measurement", + "captured_bytes": 1296, + "log_sha256": "b8effeccefde02cb06b78d4dd1c10814c2e6f05ae778654db8b715557873b00d", + "result": "passed-one-exact-case", + "child_pid_present": false, + "data_inventory": [ + { + "path": "completed.json", + "bytes": 1883, + "kind": "file", + "sha256": "265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d" + }, + { + "path": "intent.json", + "bytes": 1302, + "kind": "file", + "sha256": "71e5d32cd24ed95b5e5ee16caeceddef7c7786e4839ac1bb16a0a75d2720e946" + }, + { + "path": "jacobi-pcg-v1-1792543035724545053-59751-0.sqlite", + "bytes": 32768, + "kind": "file", + "sha256": "ea8ccde4ec9a94a218443dc2570212f37cd7439c6af73e56d934f73fd4d13279" + }, + { + "path": "member-0-finished.json", + "bytes": 1883, + "kind": "file", + "sha256": "265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d" + }, + { + "path": "member-0-started.json", + "bytes": 1691, + "kind": "file", + "sha256": "29068e832fcdafa3a61e1ac2dd809b1b36fa5515acaf32aaa2ab76a94b94b25e" + } + ], + "capture_inventory_before_receipt": [ + { + "path": "combined.log", + "bytes": 1296, + "kind": "file", + "sha256": "b8effeccefde02cb06b78d4dd1c10814c2e6f05ae778654db8b715557873b00d" + }, + { + "path": "preflight-seals.json", + "bytes": 1087, + "kind": "file", + "sha256": "9522942fc315793a41099554858a400d81d7fef658be01a733631f0912656465" + }, + { + "path": "supervisor.py", + "bytes": 7733, + "kind": "file", + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + } + ], + "temporary_file_peak": "unknown; final inventory is not aggregate or anonymous peak enforcement" +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/preflight-seals.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/preflight-seals.json new file mode 100644 index 00000000..8003f55d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/preflight-seals.json @@ -0,0 +1,28 @@ +{ + "case": "native", + "command": [ + "/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f", + "force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2", + "--exact", + "--ignored", + "--test-threads=1", + "--nocapture" + ], + "limits": { + "wall_timeout_seconds": 30, + "cpu_seconds": 20, + "address_space_bytes": 536870912, + "per_file_and_log_bytes": 1048576, + "term_grace_seconds": 1, + "core_bytes": 0 + }, + "source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "data": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/DATA", + "capture": "/workspace/scratch/rheon-ui-force-worker-execution-20261021/native/capture", + "test_body_invocation_attempted": false, + "actions": [], + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "binary_bytes": 480456920, + "source_manifest_sha256": "603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc", + "supervisor_sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/supervisor.py b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/supervisor.py new file mode 100644 index 00000000..0fd7745c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/native/supervisor.py @@ -0,0 +1,126 @@ +"""One admitted exact Worker/SQLite case. Preserve every remaining artifact.""" +import hashlib, json, os, re, resource, signal, subprocess, sys, time +from pathlib import Path +REPO=Path('/workspace/Rheon-ui-authoring') +BINARY=Path('/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f') +SEAL='af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5' +REV='3b3fccea3657d711897130643a478adb48b5edd0' +NAMES={ +'native':'force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2', +'material':'force_runtime_integration_tests::material_force_dispatch_records_immutable_scene', +} +def digest(path): + with path.open('rb') as file: return hashlib.file_digest(file,'sha256').hexdigest() +def inventory(root): + result=[] + for path in sorted(root.rglob('*')): + meta=path.lstat() + item={'path':str(path.relative_to(root)),'bytes':meta.st_size} + if path.is_symlink(): item['kind']='symlink' + elif path.is_dir(): item['kind']='directory' + elif path.is_file(): item.update(kind='file',sha256=digest(path)) + else: item['kind']='other' + result.append(item) + return result +case=sys.argv[1] +assert case in NAMES +root=Path('/workspace/scratch/rheon-ui-force-worker-execution-20261021') +root.mkdir(exist_ok=True) +case_root=root/case +case_root.mkdir(exist_ok=False) # refuses duplicate execution, including prior failures +DATA=case_root/'DATA';CAPTURE=case_root/'capture' +DATA.mkdir();CAPTURE.mkdir() +assert DATA.resolve()==DATA and CAPTURE.resolve()==CAPTURE +log=CAPTURE/'combined.log';receipt=CAPTURE/'disposition.json' +command=[str(BINARY),NAMES[case],'--exact','--ignored','--test-threads=1','--nocapture'] +limits={'wall_timeout_seconds':30,'cpu_seconds':20,'address_space_bytes':536870912,'per_file_and_log_bytes':1048576,'term_grace_seconds':1,'core_bytes':0} +state={'case':case,'command':command,'limits':limits,'source_commit':REV,'data':str(DATA),'capture':str(CAPTURE),'test_body_invocation_attempted':False,'actions':[]} +child=None +start=None +failure=None +try: + assert subprocess.check_output(['git','rev-parse','HEAD'],cwd=REPO,text=True).strip()==REV + assert not subprocess.check_output(['git','status','--porcelain'],cwd=REPO,text=True) + assert digest(BINARY)==SEAL + source_manifest=REPO/'docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/source-files.json' + for item in json.loads(source_manifest.read_text()): + assert digest(REPO/item['file'])==item['sha256'],item['file'] + listed=(REPO/'docs/plans/ui-refinement/evidence/force-worker-preparation-20261021/rheon-ui-force-worker-list2.log').read_text().splitlines() + assert listed.count(NAMES[case]+': test')==1 + state.update(binary_sha256=SEAL,binary_bytes=BINARY.stat().st_size,source_manifest_sha256=digest(source_manifest),supervisor_sha256=digest(Path(__file__))) + (CAPTURE/'preflight-seals.json').write_text(json.dumps(state,indent=2)+'\n') + (CAPTURE/'supervisor.py').write_bytes(Path(__file__).read_bytes()) + env=dict(os.environ,RHEON_UI_FORCE_TEST_ROOT=str(DATA),TMPDIR=str(DATA),SQLITE_TMPDIR=str(DATA)) + def bound(): + for key,value in [(resource.RLIMIT_CPU,20),(resource.RLIMIT_AS,536870912),(resource.RLIMIT_FSIZE,1048576),(resource.RLIMIT_CORE,0)]: + resource.setrlimit(key,(value,value)) + assert resource.getrlimit(key)==(value,value) + def group_signal(sig): + # An unreaped Popen child reserves its PID; never signal after reaping. + assert child.returncode is None + try: os.killpg(child.pid,sig) + except ProcessLookupError: pass + usage_before=resource.getrusage(resource.RUSAGE_CHILDREN) + with log.open('xb') as output: + start=time.monotonic() + state['test_body_invocation_attempted']=True + child=subprocess.Popen(command,cwd=REPO,env=env,stdout=output,stderr=subprocess.STDOUT,start_new_session=True,preexec_fn=bound) + state['pid']=child.pid + try: child.wait(timeout=max(0.0,start+30-time.monotonic())) + except subprocess.TimeoutExpired: + state['timed_out']=True;state['actions'].append('TERM owned child group');group_signal(signal.SIGTERM) + try: child.wait(timeout=1) + except subprocess.TimeoutExpired: + state['actions'].append('KILL owned child group');group_signal(signal.SIGKILL) + try: child.wait(timeout=2) + except subprocess.TimeoutExpired: state['unreaped']=True + state['elapsed_seconds_including_teardown']=time.monotonic()-start + state['exit_code']=child.poll() + try: + os.killpg(child.pid,0) + state['group_present_after_wait']=True + state['post_reap_group_probe']='unexpected group ID present; ownership unproven, not signaled' + except ProcessLookupError: state['group_present_after_wait']=False + usage_after=resource.getrusage(resource.RUSAGE_CHILDREN) + state['observed_child_cpu_seconds']={'user':usage_after.ru_utime-usage_before.ru_utime,'system':usage_after.ru_stime-usage_before.ru_stime} + state['individual_peak_memory']='not measured; address-space limit is not a measurement' + data=log.read_bytes() + state.update(captured_bytes=len(data),log_sha256=digest(log)) + assert not state.get('timed_out',False) and not state.get('unreaped',False) + assert state['exit_code']==0 and not state['group_present_after_wait'] + assert b'running 1 test' in data and NAMES[case].encode() in data + assert b'1 passed; 0 failed; 0 ignored;' in data + items=inventory(DATA) + assert all(item['kind']=='file' for item in items) + assert len(items)<=8 and sum(item['bytes'] for item in items)<=8*1024*1024 + assert all(item['bytes']<=1048576 for item in items) + db=[item for item in items if re.fullmatch(('jacobi-pcg-v1' if case=='native' else 'material')+r'-[0-9]+-[0-9]+-[0-9]+\.sqlite',item['path'])] + assert len(db)==1 + expected={db[0]['path']} | ({'intent.json','member-0-started.json','member-0-finished.json','completed.json'} if case=='native' else set()) + assert {item['path'] for item in items}==expected + state['result']='passed-one-exact-case' +except BaseException as error: + failure=repr(error);state['result']='failed-or-uncertain';state['failure']=failure +finally: + if child is not None and child.poll() is None: + state['actions'].append('TERM still-running owned child group in final disposition') + try: os.killpg(child.pid,signal.SIGTERM) + except ProcessLookupError: pass + try: child.wait(timeout=1) + except subprocess.TimeoutExpired: + state['actions'].append('KILL still-running owned child group after grace') + try: os.killpg(child.pid,signal.SIGKILL) + except ProcessLookupError: pass + try: child.wait(timeout=2) + except subprocess.TimeoutExpired: state['unreaped']=True + state['child_pid_present']=child is not None and Path('/proc',str(child.pid)).exists() + if state['child_pid_present'] or state.get('unreaped',False): + state['result']='failed-or-uncertain' + state['final_process_identity_note']='PID path remains or direct child unreaped; no post-reap signaling' + for label,path in [('data_inventory',DATA),('capture_inventory_before_receipt',CAPTURE)]: + try: state[label]=inventory(path) + except BaseException as error: state[label+'_error']=repr(error);state['result']='failed-or-uncertain' + state['temporary_file_peak']='unknown; final inventory is not aggregate or anonymous peak enforcement' + receipt.write_text(json.dumps(state,indent=2)+'\n') + print(json.dumps(state,indent=2)) +assert state['result']=='passed-one-exact-case' and not state.get('child_pid_present',False) diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/post-exit-artifact-index.json b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/post-exit-artifact-index.json new file mode 100644 index 00000000..6114f687 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/post-exit-artifact-index.json @@ -0,0 +1,81 @@ +{ + "source_commit": "3b3fccea3657d711897130643a478adb48b5edd0", + "binary_sha256": "af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5", + "cases_executed_once": [ + "native", + "material" + ], + "files": [ + { + "file": "material/DATA/material-1792543046180951263-59778-0.sqlite", + "bytes": 49152, + "sha256": "17ace71c0ed82ea905b11b90fdb1ff0413afddb5f700ad4a84a249abb26c14d5" + }, + { + "file": "material/capture/combined.log", + "bytes": 567, + "sha256": "822087d8721d2a3da1fe4bb09de5346931338c9011cb0ff291bf1a9609f33d3d" + }, + { + "file": "material/capture/disposition.json", + "bytes": 2469, + "sha256": "4f06fc37a1d1c07b89e6dfe2e674a8e7b098f29ca17081f8bd0f3c7f7c5ef819" + }, + { + "file": "material/capture/preflight-seals.json", + "bytes": 1093, + "sha256": "708130c2b29d3752caaf9c1684565b430c3d144d4489b4f111a1cf01cf8c8ebc" + }, + { + "file": "material/capture/supervisor.py", + "bytes": 7733, + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + }, + { + "file": "native/DATA/completed.json", + "bytes": 1883, + "sha256": "265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d" + }, + { + "file": "native/DATA/intent.json", + "bytes": 1302, + "sha256": "71e5d32cd24ed95b5e5ee16caeceddef7c7786e4839ac1bb16a0a75d2720e946" + }, + { + "file": "native/DATA/jacobi-pcg-v1-1792543035724545053-59751-0.sqlite", + "bytes": 32768, + "sha256": "ea8ccde4ec9a94a218443dc2570212f37cd7439c6af73e56d934f73fd4d13279" + }, + { + "file": "native/DATA/member-0-finished.json", + "bytes": 1883, + "sha256": "265e2b4885bcbe41d7af274d42d762f2691fd591511462166c96fe3fa5779b3d" + }, + { + "file": "native/DATA/member-0-started.json", + "bytes": 1691, + "sha256": "29068e832fcdafa3a61e1ac2dd809b1b36fa5515acaf32aaa2ab76a94b94b25e" + }, + { + "file": "native/capture/combined.log", + "bytes": 1296, + "sha256": "b8effeccefde02cb06b78d4dd1c10814c2e6f05ae778654db8b715557873b00d" + }, + { + "file": "native/capture/disposition.json", + "bytes": 3153, + "sha256": "9bb83d4450934309e912903f6d3a9b0c0493007dcd518cd8f717d732eec2bbb1" + }, + { + "file": "native/capture/preflight-seals.json", + "bytes": 1087, + "sha256": "9522942fc315793a41099554858a400d81d7fef658be01a733631f0912656465" + }, + { + "file": "native/capture/supervisor.py", + "bytes": 7733, + "sha256": "b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444" + } + ], + "index_excludes_itself": true +} diff --git a/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/supervisor-review.md b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/supervisor-review.md new file mode 100644 index 00000000..ea615386 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/force-worker-execution-20261021/supervisor-review.md @@ -0,0 +1,15 @@ +# One-shot Worker/SQLite supervisor review + +Review only; no invocation, repository write, test/binary/source mutation or numerical/SQLite operation was performed. Repository HEAD readback is clean `3b3fccea3657d711897130643a478adb48b5edd0`. The parent expressly authorized only the two pinned ignored cases once each, serially, with stop on first failure and preserved outputs. This report is outside the repository to preserve that preflight. + +Initial/follow-up script snapshots `eb1aee5b12dab92966af1dcbbce1ec5d177c60587bcddfe995e3ee0ee0d7c88a` and `8e4e1fc14c7077018ccbade09599f11ca82056e229fe175a94037973204c9662` were inspected. Admission is currently blocked by post-reap group signaling: `child.wait`/`poll` may reap the leader, after which signaling `killpg(child.pid, ...)` can reach a reused unrelated group. TERM grace does not establish identity. These admitted test helpers spawn threads and no subprocesses; an unexpected post-reap group observation should be recorded as failed/uncertain without signaling that unproven identity. Timeout/finally signals may target the known unreaped direct child group. The owner has been notified before any execution. + +Also normalize final receipt to failed-or-uncertain when unreaped/child_pid_present is reported, rather than leaving result=passed and relying on a later assertion to reject it. A post-reap `/proc/PID` observation cannot by itself identify an owned survivor, so describe it conservatively. + +Otherwise inspected scope matches the authorized one-shot envelope: hardcoded exact names/args, clean pinned HEAD, pinned executable SHA `af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5`, source manifest verification and exact enumeration; fresh non-replaced per-case DATA/capture siblings; TMPDIR/SQLITE_TMPDIR same as DATA; no cleanup; child 20 s CPU/512 MiB AS/1 MiB file+log/core-zero limits with readback; 30 s timeout plus TERM/KILL teardown; retained raw logs, seals and disposition; unconditional post-exit inventories even after failures. Source-known file expectations are checked only after success and the script explicitly retains the unproven SQLite transient/anonymous peak limitation. + +Run ordinary Python, not `-O` (assertions are admission gates), and preserve the pinned binary without concurrent rebuilds. The supervisor is trusted fixed-fixture orchestration, not a general security sandbox or a hard process-disappearance/aggregate-disk guarantee. Resource usage delta covers only the intended single test child after git preflight; no individual peak-memory measurement is inferred from limits. Receipt/inventory write errors or unreaped state are failed/uncertain outcomes; stop without retry or tuning. Material may be invoked only after the owner verifies the native case succeeded, not concurrently or after an uncertain first result. + +Final re-read supersedes the blocked snapshots: script SHA-256 `b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444` removes all post-reap signals. Signal-zero probes only record unexpected group presence; no ownership is inferred and receipt acceptance refuses it. Timeout/finalization signals occur only while the direct child is unreaped, and final PID/unreaped state changes the JSON result to failed-or-uncertain. The filename matcher correctly matches one escaped `.sqlite` suffix. Both blocking findings are resolved. + +Admitted under the parent's explicit execution authority only for native once followed by material once after verified native success; ordinary Python, exact pinned revision/binary/source closure, no concurrent rebuilds, new predeclared DATA/capture roots, serial invocation, stop on any failure/uncertainty, preserve inventory and do not retry/tune. Clean HEAD `3b3fccea3657d711897130643a478adb48b5edd0` was independently read back. The supervisor itself revalidates all pins before dispatch. No repository report was written and no invocation was performed by the reviewer. Resource/transient/teardown qualifications above remain in force; this is not admission for any other case or future gate-policy enablement. diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 13a0bdf4..69a43cc2 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -536,3 +536,31 @@ assertion is not a hardaggregatebound. No newsecurity/mount/cgroup requirements. Future typed-effective-target gate-removal scope independently reviewed, no code until boundedruntime+reachablegatednativeacceptance. ActualenabledUIlaunch needs separatecandidate-sourcevalidation aftereligibility. Productiongate stays. + + +## Two bounded T5 Worker cases —2026-10-21 + +On sealed3b3fccea3657d711897130643a478adb48b5edd0 / tree4cdf4b221bb47704acfcda6b682453ed60344908, +executed the two separately admitted exact ignored cases ONCE each, native then +material, from binarySHAaf2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5. +Each passed1/exit0; no retry, timeout, signal or observed surviving ownedPID/group. +Reviewed supervisor applied30s deadline/20CPU/512MiBAS/1MiBper-file+log/core0, +TERM then1sKILL available. One ownedRayonworker. Fresh separate DATA/capture roots +retained without test cleanup. Native final5files39527bytes/1296byte log; +material1file49152bytes/567byte log. Elapsed0.064929/0.032914s includingteardown; +observedchildCPU0.028593/0.020462s. Limits are not memory measurements; SQLite +transient/anonymous peak unknown, final8file8MiB guard is not aggregate quota. + +Independent [result review](reports/force-worker-runtime-results-review.md) verifies +14artifact hashes/source/binary seals. [Receipts](evidence/force-worker-execution-20261021/manifest.json) +copy exact rawlogs/disposition/preflight/supervisor; DBs and480456920byte executable +remain in scratch with hashes. Archive producer revision is build-unspecified; +external seals establish3b custody, not embedded authenticated revision provenance. +Material verifies retained typedRequest+coldForceScene only: no serialized material +coldrerunAPI. Native prepared rerunclone never launches and retains oldoutputpaths. +These tiny directional acceleration cases qualify dispatch/effect/custody/coldreadback +only, not scientific parity/Cancel/generalforce/UIacceptance. Production remains gated. + +Parent separately reports exact1b377742 enabledhover/resize/Scene/normalclose exit0 +and synthetic900x650150% timeline labels/popup/captions/DraftRetained passed. +Viewport gestures remain pending; this owner performed no new native session. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index ff857eb0..228a50e4 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -155,7 +155,7 @@ Generated logs/screenshots/transfer packets remain outside Git. | --- | --- | --- | --- | --- | | S1 | Empty idle timeline fits <=70 logical px in900/1250px windows with the preceding280px sidebar reserved; selected-frame captions remain present and may naturally wrap/grow; actual Play/Pause/step/Live input keeps prior transitions | integration / isolated egui painting/input / automated | pending | Four exact probes pass: idle48px versus106px, focused speed editing and native-track/literal-report layouts. Native/configured/popup-keyboard coverage remains open | | S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | Canonical authoring source and five exact UI probes pass; viewport picking/native remain pending | -| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent source implemented and gated; desktop runtime/archive validation pending | +| S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent source implemented and gated; two bounded3b Worker cases pass; native acceptance pending | | S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | T4 reviewed geometry/transaction source and six exact inert tests pass; full Viewport gesture/native evidence pending | | S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; prior426fd25 images do not cover this source | | S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T5 thirteen exact inert tests, locked build/check, strict production Clippy and independent source review pass; no archive workload or scientific certificate | @@ -207,11 +207,11 @@ Ordinary lint/layout failures stay within the current exact slice and retain log Canonical force source and desktop scene/dispatch/frozen intent/load/rerun source are implemented in the isolated owner branch. Production authored-force runs stay -gated pending separately bounded desktop runtime/archive execution; configured, +gated pending relevant native acceptance; two bounded3b runtime/archive cases passed. Configured, provider/session/transport/free-surface propagation remains explicitly unsupported. Minimal Inspector recorder ownership is now assigned and source-reviewed. No core recording/solver source changed here. Local native display is unavailable; the -existing validator owns exact8148fa2 native work and actual JPEG delivery separately. +existing validator owns exact1b377742 native gestures and actual JPEG delivery separately. Live cancellation lacks required existing containment and stays parked. No mount, cgroup, permission, credential or network changes are authorized. @@ -235,12 +235,16 @@ validated force metadata, manifest v1 supports independently valid mixed members Retained tree/properties and viewport share one render-frame track/document; track intents paint consistently next frame and clear reused-ID selection. -No numerical/SQLite/runtime or new native acceptance is claimed. Thirteen exact -pure/Context tests pass after list-only enumeration. Two ignored worker/SQLite cases are now compiled and individually enumerated -(201total), with [bounded contract](reports/runtime-force-validation-contract.md) -and independent worker-test reviews. Neither case has executed. Material rerun +The historical source checkpoint had thirteen exact pure/Context passes and201 +list-only names. Subsequently both individually admitted ignored Worker/SQLite cases +ran once serially on sealed3b3fccea3657d711897130643a478adb48b5edd0 and passed under the +[bounded contract](reports/runtime-force-validation-contract.md). The +[independent result review](reports/force-worker-runtime-results-review.md) and +[receipts](evidence/force-worker-execution-20261021/manifest.json) delimit evidence: +one native8cell and one64particle track, one interval, exact frozen force association, +coarse effect guards and coldreadback, no general/scientific/cancellation acceptance. Material rerun coverage is retained typed Request plus cold force-scene association; a production serialized material cold-rerun API does not exist. Keep the production -force gate until separately admitted runtime/archive evidence qualifies this source. +force gate until relevant native acceptance and a separately reviewed enabling change. Earlier native425a986/8148fa2 and synthetic harness artifacts retain their own exact source/evidence; none accepts this changed desktop closure or genuine cancellation. diff --git a/docs/plans/ui-refinement/reports/force-worker-runtime-results-review.md b/docs/plans/ui-refinement/reports/force-worker-runtime-results-review.md new file mode 100644 index 00000000..3aca5254 --- /dev/null +++ b/docs/plans/ui-refinement/reports/force-worker-runtime-results-review.md @@ -0,0 +1,52 @@ +# Independent one-step force Worker results review + +## Disposition + +Accept the two recorded outcomes as narrowly scoped real-Worker dispatch/effect/custody and cold-readback evidence for the sealed cases below. Both were separately authorized and executed once serially by the owner; each raw log records one exact pass. No retry, tuning or additional workload is supplied or requested by this review. The production force gate remains closed. Native interaction and broader runtime/scientific qualification remain incomplete. + +The reviewer read logs, receipts, source assertions and manifests, and hashed artifact bytes. No executable/test was run or rerun; no SQLite database was opened or queried; no numerical/model/native workload or artifact cleanup occurred. Only this report was written after the runtime clean-tree preflight had completed. + +## Custody and independent checks + +Execution revision: `3b3fccea3657d711897130643a478adb48b5edd0`. The preparation source manifest SHA-256 is `603a0edf75b255b6ba217aaa6e72632a5e3ab16eb8997fe0013fa7a1175fbafc`; all 278 entries were independently rehashed against the current source. Test module SHA-256 remains `da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584`. The complete helper/assertion closure was reviewed before execution and re-read against the recorded results. + +The preserved executable `/workspace/scratch/rheon-ui-force-worker-execution-20261021/sealed-runtime-tests-3b3fcce.bin` is 480,456,920 bytes and independently hashes to `af2da1539521e10407ac73b11cd58494118aa93a3f99ed529b11697ed85727b5`, matching both preflight/disposition receipts. Each captured supervisor copy matches the separately reviewed `b7f13a3bdee65a6d4345ddbc0a0eaa7ddef7d64e4224633d63e16f8db1046444` script. Its post-reap signaling defect was repaired before execution; no signal action occurred in either case. + +The post-exit index `/workspace/scratch/rheon-ui-force-worker-execution-20261021/post-exit-artifact-index.json`, SHA-256 `4b9dc8a8188671cb13ce67a1585a589f012ae8cf7e92a7918b067d59caea8a85`, was independently checked against all fourteen indexed DATA/capture artifacts by byte size and SHA-256. Both raw logs and their receipts agree on exact name, one pass/zero failures/200 filtered out, pinned source/binary/supervisor, exit zero, no timeout, no signals and no group/PID survivor observation. The two recorded child PIDs were absent on independent readback. This is not a guarantee against PID reuse or a general process-supervisor certification. + +The preparation manifest's earlier `new_test_bodies_executed=false` describes its historical compile/list checkpoint. These separate execution receipts now supply the two authorized results without rewriting that historical evidence. + +## Recorded cases and resources + +| Case | Exact ignored test suffix | Supervisor elapsed, including teardown | Observed child CPU, user+system | Final DATA | Captured combined log | +| --- | --- | ---: | ---: | ---: | ---: | +| Native | `native_force_dispatch_records_and_cold_binds_v2` | 0.064929 s | 0.028593 s | 5 files; 39,527 bytes | 1,296 bytes | +| Material | `material_force_dispatch_records_immutable_scene` | 0.032914 s | 0.020462 s | 1 file; 49,152 bytes | 567 bytes | + +Both exact names are under `force_runtime_integration_tests`. Their commands use `--exact --ignored --test-threads=1 --nocapture`, never a broad filter. Raw evidence is under `/workspace/scratch/rheon-ui-force-worker-execution-20261021/{native,material}/{DATA,capture}`. Native ran before material; the preserved per-case roots and source-reviewed one-shot supervisor refuse duplicate use of those destinations. + +Applied child limits recorded by the reviewed supervisor are a 30 s timeout deadline, 20 s CPU, 512 MiB address space, 1 MiB per-file/combined-log limit and zero core size, with TERM→1 s grace→KILL teardown available. Neither case reached a timeout or required teardown signals. CPU values are the single child interval's RUSAGE_CHILDREN deltas after git preflight. Individual peak memory was not measured; 512 MiB is a limit, not consumption. DATA sizes are final retained logical bytes, not peak disk blocks, cumulative I/O or SQLite temporary/anonymous-file use. SQLite transient peak remains unknown; the success-only eight-file/8 MiB inventory assertion is not an aggregate filesystem quota. Existing protocol unlinking of journals/staging is not test cleanup and does not preserve historical transient files. + +## What the passing assertions establish + +Both cases passed through actual Request→Worker::start→owned Worker/Rayon execution, with threads=1, join before result assertions, progress 1, one result row and two ordered in-memory previews. They used the same frozen enabled version-1 Global Directional acceleration document (`[0.25,0,0]` m/s²), canonical backend adapter and recorder/publication association. The temporary cfg(test) gate bypass did not bypass the remaining eligibility, budget, snapshot or binding validation. No App/UI start route or production gate was exercised. + +**Native:** one JacobiPcgV1 track, 2³ cells, density 2 kg/m³, no source/emitter/model, one requested 1e-6 s interval and pressure iteration cap 100. The source asserts zero constructor velocities, absent constructor interval pressure, accepted interval starting at zero, eight finite pressure values within the predeclared ±0.05 Pa guard around `(index%2)×0.25 Pa`, force-qualified controls and accepted dt exactly equal to requested dt. The pass supports this coarse nonzero dispatch symptom; actual pressure values were not printed, independently extracted or scientifically re-evaluated here. + +The executed cold path required the finished immutable member to open Ready, verified finalized pin/run/spec/declaration association, completed status/count 2/generation 1 and exact force bytes on constructor and accepted records. Preview/result timestamps match the accepted record. Mutating a separate draft was followed by archive rereads requiring original force bytes. The native prepared-rerun clone preserved the stored v2 intent and cleared the attempt without starting a second Worker; it retains old output/receipt paths and is not execution-ready or evidence of production rerun/numerical equivalence. + +**Material:** a 1 m domain with 2³ preview/grid cells, 4³=64 initially stationary emitted-fluid water particles, no emitter/collision, gravity `[0,-1,0]` m/s² and one public step (canonical internal preparation bound 16). The source asserts 64 initial/accepted particles; finite positive actual dt≤requested dt; finite positive mass and net X momentum change within the predeclared 50–150% of initial mass×0.25 m/s²×actual dt. This is a coarse dispatch-effect guard, not SPH accuracy/force-equivalence validation. The pass also checks cold header configuration/geometry, both exact force documents, completed/count/generation, actual accepted timing/diagnostics and preview/result association. + +The material Request clone is retained typed intent with its force rebound from cold metadata, followed by archive rereads after mutating an independent draft. Material has no serialized FrozenSpec/cold-rerun API: this does not reconstruct all controls from archive text, validate production cold material rerun or execute a second step/Worker. + +## Retained artifacts and provenance limitation + +Native final DATA contains the four expected immutable JSON manifests and one 32,768-byte SQLite archive, SHA-256 `ea8ccde4ec9a94a218443dc2570212f37cd7439c6af73e56d934f73fd4d13279`. Material retains one 49,152-byte archive, SHA-256 `17ace71c0ed82ea905b11b90fdb1ff0413afddb5f700ad4a84a249abb26c14d5`. No unexpected final data name or sidecar was recorded. Hash verification was byte-only; this reviewer did not independently perform the databases' SQL/decoder checks. Those checks are evidence from the passing sealed assertions. + +The native finished manifest's expected source revision is `rheon-desktop-saved-carrier/0.1.0; source=build revision unspecified; tracked-material=unspecified`. Thus archive/header association matches the producer's declared string, but the archive is not self-identifying as revision 3b3. The external clean-source/binary/preflight/index seals supply the reviewed source custody. Do not describe this as embedded authenticated revision provenance or silently rewrite the archive; no rerun is authorized to change it. + +## Remaining acceptance boundaries + +These results cover one enabled Global Directional acceleration field, one native Jacobi track and one default-water material track. They do not exercise other field kinds/units/supports/time windows, emitter/collision combinations, authored-empty metadata, mixed saved members, configured/provider/session/free-surface paths, failure/unrecorded tails, timeout/kill behavior, interrupted Cancel/join, legacy equivalence, general field accuracy or scientific validation. Prior backend or pure codec evidence must remain separately identified. + +Native force launch, actual progress/Cancel, retained versus draft gestures, picking/handles, keyboard/accessibility and cold material rerun remain unqualified. A future typed eligibility draft/pure-test review may be prepared separately; these passes do not authorize enabling the production gate, removing the test bypass, another workload or further native execution. Preserve the exact artifacts and prior evidence without cleanup or tuning. diff --git a/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md index 82936f62..48f98816 100644 --- a/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md +++ b/docs/plans/ui-refinement/reports/runtime-force-validation-contract.md @@ -1,4 +1,12 @@ -# T5 bounded worker validation contract — prepared, NOT EXECUTED +# T5 bounded worker validation contract — historical preparation, executed once + +Execution addendum (2026-10-21): the two exact cases below subsequently ran once +serially on sealed3b3fccea3657d711897130643a478adb48b5edd0, and passed. This document's +preparation statements and proposed limits describe the earlier checkpoint; +[the independent results review](force-worker-runtime-results-review.md) and +[immutable execution receipts](../evidence/force-worker-execution-20261021/manifest.json) +record actual outcomes, retained bytes, unknown temporary peaks and provenance limits. +No repeat invocation or production enablement follows from this addendum. This amendment supersedes the earlier direct-loop/1GiB proposal reviewed at5eab3ac. Two ignored bodies now exist in desktop/force_runtime_integration_tests.rs; list-only From 9099b150e8b80cd750db08276c51e180be4e61f6 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:48:45 +0000 Subject: [PATCH 19/25] refactor(ui): centralize force readiness with production qualification pending --- .../typed-force-policy-20261021/manifest.json | 132 +++++++++++ .../plan-review.md | 117 ++++++++++ .../rheon-ui-force-policy-accessor-exact1.log | 8 + .../rheon-ui-force-policy-accessor-exact2.log | 8 + .../rheon-ui-force-policy-check.log | 14 ++ .../rheon-ui-force-policy-check2.log | 2 + .../rheon-ui-force-policy-clippy.log | 14 ++ .../rheon-ui-force-policy-clippy2.log | 2 + .../rheon-ui-force-policy-diffcheck.log | 0 .../rheon-ui-force-policy-exact1.log | 9 + .../rheon-ui-force-policy-exact2.log | 8 + .../rheon-ui-force-policy-exact3.log | 8 + ...heon-ui-force-policy-exact4-copy-fixed.log | 8 + .../rheon-ui-force-policy-exact4.log | 21 ++ .../rheon-ui-force-policy-exact5.log | 8 + .../rheon-ui-force-policy-fmt.log | 0 .../rheon-ui-force-policy-fmt2.log | 0 .../rheon-ui-force-policy-list1.log | 209 ++++++++++++++++++ .../rheon-ui-force-policy-list2.log | 209 ++++++++++++++++++ .../rheon-ui-force-policy-list3.log | 209 ++++++++++++++++++ .../rheon-ui-force-policy-list4.log | 209 ++++++++++++++++++ .../rheon-ui-force-policy-list5.log | 209 ++++++++++++++++++ .../rheon-ui-force-policy-plan-check.log | 1 + docs/plans/ui-refinement/execution-ledger.md | 24 ++ docs/plans/ui-refinement/plan.md | 21 ++ .../typed-force-policy-source-review.md | 59 +++++ src/bin/desktop/force_run_policy.rs | 36 +++ src/bin/desktop/force_run_policy_tests.rs | 166 ++++++++++++++ src/bin/desktop/force_runtime_ui_tests.rs | 7 +- src/bin/desktop/scene_ui.rs | 34 ++- src/bin/rheon-desktop.rs | 63 ++++-- 31 files changed, 1782 insertions(+), 33 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/plan-review.md create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact1.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-diffcheck.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact1.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact3.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4-copy-fixed.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact5.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list1.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list2.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list3.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list4.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list5.log create mode 100644 docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-plan-check.log create mode 100644 docs/plans/ui-refinement/reports/typed-force-policy-source-review.md create mode 100644 src/bin/desktop/force_run_policy.rs create mode 100644 src/bin/desktop/force_run_policy_tests.rs diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/manifest.json b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/manifest.json new file mode 100644 index 00000000..9ca77ea5 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/manifest.json @@ -0,0 +1,132 @@ +{ + "base": "e5e6cff984d7f220b2b3a741fb8759aa542fccc3", + "runtime_evidence_source": "3b3fccea3657d711897130643a478adb48b5edd0", + "production_qualification": "Pending", + "enumerated_total": 204, + "exact_unique_pure_tests_passed": 5, + "preserved_failures": [ + "exact4 stale-copy assertion; updated to shared PENDING and exact4-copy-fixed passed", + "initial strictClippy unused draft accessor; readiness now uses same immutable accessor and check2/clippy2 pass" + ], + "new_numerical_execution": false, + "sources": { + "src/bin/rheon-desktop.rs": "2c614eb7ea5920ee450ffcb344555304146869b332d8f9fe50f8e7a7703f763c", + "src/bin/desktop/scene_ui.rs": "73c16415819c5215a797615f5893222c2ec96eebe2bc8d100b95d346bed7d208", + "src/bin/desktop/force_run_policy.rs": "086d4ee8c18ff30f2165b8622a53d649bd9eb8166bbbbcb82dde3a0eb2427cc2", + "src/bin/desktop/force_run_policy_tests.rs": "a518b47b71cb2aff5d7ce46169918c60d534c00ffccdd0ee5c90abcebbdcdebd", + "src/bin/desktop/force_runtime_ui_tests.rs": "1d242eaed1b416e3dfb2577d5610c4e0d6d0ca5040f80293681ae184090cebb9" + }, + "files": [ + { + "file": "plan-review.md", + "bytes": 7457, + "sha256": "e696a247a5f540554d4a658203f9a8f55da4d353b95f9b3701c49e250846a759" + }, + { + "file": "rheon-ui-force-policy-accessor-exact1.log", + "bytes": 413, + "sha256": "5c2e38fae8927ab6bf15220a3b4f304dd5e276ce7868e467e3f306fecbae9d02" + }, + { + "file": "rheon-ui-force-policy-accessor-exact2.log", + "bytes": 398, + "sha256": "6871aabb59fe618a18da1ebab8d57dbf0271062f20b96087744fdccb239e2001" + }, + { + "file": "rheon-ui-force-policy-check.log", + "bytes": 492, + "sha256": "7f16404930fa761a8b36bfb3c35b76a6a168dd10a33e19c6cc0c91039e8fc1a7" + }, + { + "file": "rheon-ui-force-policy-check2.log", + "bytes": 130, + "sha256": "e05b12393294b621219310fbe0c6f78c2fed1de17b2de9a5dd62ab70a72bd6af" + }, + { + "file": "rheon-ui-force-policy-clippy.log", + "bytes": 478, + "sha256": "b8b7fc67465e48be267d0e45786cca2cdf331e4bd293f3de241b1cfed9c2afbd" + }, + { + "file": "rheon-ui-force-policy-clippy2.log", + "bytes": 130, + "sha256": "b8159f92b8c812a57cba795f95263b24287dd97512c3bc9f20270a644e1273f1" + }, + { + "file": "rheon-ui-force-policy-diffcheck.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-force-policy-exact1.log", + "bytes": 462, + "sha256": "6ad3a072d9a095e958ec22d7285a230b7c8a38c02288243e34bc2cedd4b6d1cb" + }, + { + "file": "rheon-ui-force-policy-exact2.log", + "bytes": 403, + "sha256": "643268e6dfb58bcbd5c8641079004c472f796947a02effeb6c3d9ae734196375" + }, + { + "file": "rheon-ui-force-policy-exact3.log", + "bytes": 413, + "sha256": "fad64dd12767f86d9bb9d9aaa064983a08a4e1b57830dcec173b29e391913780" + }, + { + "file": "rheon-ui-force-policy-exact4-copy-fixed.log", + "bytes": 417, + "sha256": "286f14a3a666ab52ef68373f575590b06e1f1dd6399a0f83f3615490df1766ac" + }, + { + "file": "rheon-ui-force-policy-exact4.log", + "bytes": 1054, + "sha256": "80c93febaf82092e8cd929709efe081d729be121484d21f92e8f0ea4dfd0a5a6" + }, + { + "file": "rheon-ui-force-policy-exact5.log", + "bytes": 398, + "sha256": "97cf8733e1154197cb0e8a6ca1a51c014b59b86cbb19f8d116e8090660664e74" + }, + { + "file": "rheon-ui-force-policy-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-force-policy-fmt2.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-force-policy-list1.log", + "bytes": 21444, + "sha256": "8a9792df9022fe6af5e7be0807f56ae5c486b56f03e7b93fed425853fb817ab1" + }, + { + "file": "rheon-ui-force-policy-list2.log", + "bytes": 21443, + "sha256": "1f8f3b9be3b4f6d728568a04623e04bcdb4e2eaf0e59f0bb2e3ac121b1bb449f" + }, + { + "file": "rheon-ui-force-policy-list3.log", + "bytes": 21443, + "sha256": "335e5b0e012eda49da8c4f0189401612d920a007cf04124e90700aec76df5446" + }, + { + "file": "rheon-ui-force-policy-list4.log", + "bytes": 21443, + "sha256": "24dd1ce8a43805122accc63c249c21003f4b1ee9dfe4058b73890bd5f9ac325b" + }, + { + "file": "rheon-ui-force-policy-list5.log", + "bytes": 21443, + "sha256": "3d41dae9c22524cdf44afb74d71799b8ec649ac16cad35d6090cfdcb584f63f1" + }, + { + "file": "rheon-ui-force-policy-plan-check.log", + "bytes": 78, + "sha256": "d3f9de8f8ad7079de6beac331d546d3ec455a8b45f62d754ef856c01149ba378" + } + ], + "source_review_sha256": "a4242f495a28f7be564e211262d8f86776f74bdeba5159dc33941dc11ca4132b" +} diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/plan-review.md b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/plan-review.md new file mode 100644 index 00000000..9c96d444 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/plan-review.md @@ -0,0 +1,117 @@ +# Independent typed force-readiness policy plan review + +## Disposition + +Suitable for gated source preparation with the conditions below. The proposed +small module and three Target variants/two Qualification states express the +actual independent decisions without a capability framework. No production force +enablement is admitted: one shared production qualification stays Pending until +the separately owned native acceptance returns and a later enabling change is +reviewed. No numerical/SQLite probes need to be repeated for this policy slice. + +Reviewed actual readiness/dispatch seams at worktree HEAD +`3b3fccea3657d711897130643a478adb48b5edd0`: Request validation and private test +bypass; SetupWorkflow, visible run controls, start/start_configured, saved rerun; +AI comparison entry; SceneUi draft freeze/readiness and existing pure metadata +tests. Parent reports both exact Worker/SQLite cases passed once on the sealed +prior closure. This reviewer did not verify their raw receipts or rerun them. +No build, test, source edit, native or numerical/SQLite action was performed. +Only this temporary review report is written. + +## Minimal API and ownership + +`force_run_policy.rs` may own Target::{NativeCarrier, ParticleMaterial, +Unsupported}, Qualification::{Pending, Validated}, one production Pending constant +and one pure check(target, has_authored, qualification). Keep it a desktop module +with narrow visibility; no trait registry, plugin/capability framework, dynamic +evidence service or serialized new field. Existing domain validation/freezing +remains outside this policy. The policy cannot itself establish a valid document, +supported geometry, budget, scientific correctness or recording association. + +Meaning of the single presence argument must be explicit: authored metadata is +present, not physically effective. Any Some ForceSnapshot counts, including +Some(empty), disabled objects and zero strength. A legacy None does not. Request +presence is derived once from its authoritative per-track vector; saved-member +None never falls back to draft. SceneUi readiness uses its canonical draft/freeze +path, never the currently displayed Retained document. An empty ordinary draft +continues producing None under the existing launch semantics. + +For absent metadata the policy imposes no new force restriction; all ordinary +Request checks still apply. For present metadata, Unsupported refuses regardless +of qualification; otherwise Pending refuses and Validated permits only this force +eligibility decision. Thus the private test-qualified path cannot admit configured +or other unsupported ownership. Error projection should distinguish unsupported +target from pending acceptance with concise visible text; avoid parsing messages +to derive decisions. + +## Adapter and composition conditions + +- Derive Request target from the actual consumed dispatch: configured/session + presence takes Unsupported precedence, then material Some maps to + ParticleMaterial, and only the existing native carrier branch maps to + NativeCarrier. A future unknown owner must not silently default to native. +- Derive visible setup target from effective SetupWorkflow plus preview material + mode. Accepted Session remains Unsupported even when retained material mode is + true. Recompute/check at dispatch after current form updates; do not dispatch a + stale button-time qualification or independently passed enable booleans. +- `start_configured` and the AI comparison entry explicitly use Unsupported. + AI must not inherit NativeCarrier merely because its button appears under + Preview. Retain current viewport/lab force refusal; this slice does not qualify + transport/provider/free-surface or change those owners. +- `SceneUi::launch_readiness(target)` and Request validation call the same policy + using the same shared production Pending constant. Recheck before any existing + output-creating saved-spec/start/rerun path; a check only in Worker::start is too + late where manifests/directories are allocated earlier. +- Saved native rerun validates its own immutable Request/member snapshots without + consulting SceneUi or current draft, even if the latter is invalid/unsupported. + Preserve legacy None behavior, explicit Some(empty) and differing member scenes. +- Keep the existing private cfg(test) force_validation-to-Validated mapping only + for the reviewed temporary test mechanism. Default false in ordinary literals + and harnesses. Add no production flag, CLI/environment selector or mutable + qualification. The later enabling commit removes that bypass/field with its + fixture updates, rather than replacing it with another hidden gate. + +One pure shared policy does not require changing the established session/worker, +numerical adapters, run-set codec, recording, per-track resolver or retained-view +owners. Limit production writes to the new policy module/declaration and existing +main/SceneUi readiness adapters. Test fixture signature updates are mechanical; +preserve unrelated current focus/native work and execution artifacts. + +## Focused acceptance for this still-gated slice + +Use inert policy/metadata/Context evidence only, with full helper inspection, +list-only enumeration then individually exact names under the existing workflow. + +1. The policy decision table proves no-metadata legacy passage, supported Pending + refusal, test-qualified supported passage, and Unsupported refusal under both + qualification states. Cover Some(empty), disabled and zero objects at the + adapters so the presence semantics cannot accidentally become effect testing. +2. Both visible SceneUi readiness and private Request validation remain blocked + under the actual production Pending constant. Assert the constant is Pending + in this preparation checkpoint. Canonical invalid draft/snapshot validation and + existing budgets/bindings are not bypassed by a policy success. +3. Effective workflow mapping agrees with consumed Request targets. In particular + Accepted Session + retained material mode, configured Some + material Some, + and AI remain Unsupported. The test-qualified flag cannot override them. +4. Saved members keep exact force/None precedence and ignore current draft. + Refused paths emit no launch/output intent. Do not call positive start/rerun + methods that would create a Worker/files merely to test readiness. +5. Build/production lint/format and independent source review cover the changed + adapters after implementation. This report does not claim those checks passed. + +These checks make the candidate concrete while leaving production behavior +gated. Neither the earlier two passing Worker probes nor policy unit tests supply +native enabled-launch, cancellation, gesture, accessibility or scientific parity +acceptance for the changed closure. + +## Later enablement boundary + +The future enabling increment is separate: only after the stated native +prerequisites and review may production qualification change and the private +test bypass be removed with equivalent positive/negative production coverage. +Reachable gated authoring/native evidence can precede that increment; actual +force-enabled UI launch is validated afterward on the isolated candidate source +under separate bounded native ownership. This ordering avoids circularity and +does not authorize gate changes or workloads now. Rollback of the preparation +slice restores only its policy/adapters; preserve the sealed prior runtime and +native evidence unchanged. diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact1.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact1.log new file mode 100644 index 00000000..234dd33d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact1.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact2.log new file mode 100644 index 00000000..ec499d2d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-accessor-exact2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.01s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check.log new file mode 100644 index 00000000..a9e2441d --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check.log @@ -0,0 +1,14 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +warning: method `draft` is never used + --> src/bin/desktop/scene_ui.rs:79:12 + | +69 | impl SceneUi { + | ------------ method in this implementation +... +79 | pub fn draft(&self) -> &ForceScene { + | ^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: `rheon` (bin "rheon-desktop") generated 1 warning + Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.15s diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check2.log new file mode 100644 index 00000000..b76e79db --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-check2.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.15s diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy.log new file mode 100644 index 00000000..b5df8672 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy.log @@ -0,0 +1,14 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error: method `draft` is never used + --> src/bin/desktop/scene_ui.rs:79:12 + | +69 | impl SceneUi { + | ------------ method in this implementation +... +79 | pub fn draft(&self) -> &ForceScene { + | ^^^^^ + | + = note: `-D dead-code` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(dead_code)]` + +error: could not compile `rheon` (bin "rheon-desktop") due to 1 previous error diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy2.log new file mode 100644 index 00000000..f57003ba --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-clippy2.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.73s diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-diffcheck.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-diffcheck.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact1.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact1.log new file mode 100644 index 00000000..b9354f74 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact1.log @@ -0,0 +1,9 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 4.50s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact2.log new file mode 100644 index 00000000..758e5fdf --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact2.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact3.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact3.log new file mode 100644 index 00000000..2067914e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact3.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4-copy-fixed.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4-copy-fixed.log new file mode 100644 index 00000000..5887dcf0 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4-copy-fixed.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4.log new file mode 100644 index 00000000..f8807a18 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact4.log @@ -0,0 +1,21 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members ... FAILED + +failures: + +---- force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members stdout ---- + +thread 'force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members' (61493) panicked at src/bin/desktop/force_runtime_ui_tests.rs:258:5: +assertion failed: validate_request(&r).unwrap_err().contains("validation is pending") +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact5.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact5.log new file mode 100644 index 00000000..0c957a61 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-exact5.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 203 filtered out; finished in 0.00s + diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-fmt2.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list1.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list1.log new file mode 100644 index 00000000..0d64b250 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list1.log @@ -0,0 +1,209 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 22.33s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +204 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list2.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list2.log new file mode 100644 index 00000000..a2f8c4bc --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list2.log @@ -0,0 +1,209 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 6.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +204 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list3.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list3.log new file mode 100644 index 00000000..59507d47 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list3.log @@ -0,0 +1,209 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 9.48s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +204 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list4.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list4.log new file mode 100644 index 00000000..289e7377 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list4.log @@ -0,0 +1,209 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 4.73s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +204 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list5.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list5.log new file mode 100644 index 00000000..42932615 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-list5.log @@ -0,0 +1,209 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 5.09s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +204 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-plan-check.log b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-plan-check.log new file mode 100644 index 00000000..11274108 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/typed-force-policy-20261021/rheon-ui-force-policy-plan-check.log @@ -0,0 +1 @@ +Pinned structural plan check passed; no standards-engine certificate claimed. diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 69a43cc2..27982746 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -564,3 +564,27 @@ only, not scientific parity/Cancel/generalforce/UIacceptance. Production remains Parent separately reports exact1b377742 enabledhover/resize/Scene/normalclose exit0 and synthetic900x650150% timeline labels/popup/captions/DraftRetained passed. Viewport gestures remain pending; this owner performed no new native session. + + +## Typed force eligibility preparation —2026-10-21 + +Over separate evidence-only e5e6cff, replaced duplicated readiness strings with one +small Target(NativeCarrier/ParticleMaterial/Unsupported) and Qualification policy. +PRODUCTION remains Pending; private cfg(test) admission remains test-only. Any Some +snapshot counts, including empty/disabled/zero. Configured overrides material; +Unsupported never becomes supported via qualification. Visible controls, private +starts, AI and authoritative Request use effective targets. Saved-member bindings, +per-track snapshots and physics/recording owners unchanged. Invalid draft freezes +refuse; retained display never supplies new draft launch intent. + +Independent plan and full-helper source reviews plus204-name list-only enumeration; +five individually selected exact pure cases pass. Initial fourth pure case failed +stale wording; shared PENDING assertion repaired, exact affected case passed. +StrictClippy initially found now-unused draft accessor; readiness uses that same +immutable accessor, affected two exact cases reran/passed. Check2/strictClippy2/fmt2 +pass. All failures/logs preserved in evidence/typed-force-policy-20261021. No numerical, +SQLite, Worker or native execution in this policy slice. OriginalM2a settings hash +0937b5e293fdd024641f46382900debac0b6f8c263d2a3c5d61fc0f8191dc102 unchanged. +Gate enablement is a later reviewed increment after native prerequisites, with +private bypass removal and separately bounded candidate validation. No M1/fullforce +or accessibility/cancellation acceptance inferred from these scalar/Context tests. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 228a50e4..3fa2e55e 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -248,3 +248,24 @@ serialized material cold-rerun API does not exist. Keep the production force gate until relevant native acceptance and a separately reviewed enabling change. Earlier native425a986/8148fa2 and synthetic harness artifacts retain their own exact source/evidence; none accepts this changed desktop closure or genuine cancellation. + + +## T5 typed eligibility preparation and later enablement + +One desktop-only policy resolves effective Target (NativeCarrier, ParticleMaterial, +Unsupported) separately from Qualification (Pending, Validated). Production stays +Pending. Force presence is authoritative Some metadata, even empty/disabled/zero; +legacy None remains legacy. Configured/session/provider/free-surface/AI never gain +eligibility from qualification. Request and visible/private setup adapters use the +same decision, preserving canonical validation/member binding and per-track custody. +[Plan review](evidence/typed-force-policy-20261021/plan-review.md), +[source review](reports/typed-force-policy-source-review.md) and +[exact pure/static receipts](evidence/typed-force-policy-20261021/manifest.json) +cover this preparation, without attributing prior3b runtime results to this new closure. + +The next small enabling increment may change qualification only after separately +reviewed native gesture prerequisites; remove the private test bypass/field and +update its fixtures. Then validate actual force-enabled App launch on that exact +isolated candidate under separately bounded native ownership. No live execution +or gate change is implied by this plan. Rollback only the policy/adapters increment; +preserve original UI/native/runtime evidence, archives and historical hash contracts. diff --git a/docs/plans/ui-refinement/reports/typed-force-policy-source-review.md b/docs/plans/ui-refinement/reports/typed-force-policy-source-review.md new file mode 100644 index 00000000..e2fc1b68 --- /dev/null +++ b/docs/plans/ui-refinement/reports/typed-force-policy-source-review.md @@ -0,0 +1,59 @@ +# Independent typed force policy source review + +## Disposition and scope + +Suitable for a separate still-gated policy checkpoint. Reviewed preparation began over HEAD `3b3fccea3657d711897130643a478adb48b5edd0` in `/workspace/Rheon-ui-authoring`, against `/tmp/rheon-ui-typed-policy-plan-review.md` (SHA-256 `e696a247a5f540554d4a658203f9a8f55da4d353b95f9b3701c49e250846a759`). Final receipt HEAD is `e5e6cff984d7f220b2b3a741fb8759aa542fccc3`, the separately committed prior runtime evidence; the policy source remains the local increment bound below. `PRODUCTION_QUALIFICATION` is still Pending. No production force enablement, numerical/SQLite rerun or new native acceptance is admitted. No material source blocker remains; focused pure/static receipts are recorded below. + +The reviewer read complete changed source/helper paths and wrote only this report. No build, test, solver/model/evaluator/native action, Worker, SQLite operation, output fixture or cleanup was executed. The two earlier authorized Worker results remain bound to their prior source/binary and are not automatically evidence for this changed closure. + +## Shared policy and adapters + +`force_run_policy` has three explicit Target values and two Qualification values, with one pure check and one production Pending constant. Absent metadata adds no new force restriction; other existing Request validation remains. Present metadata on Unsupported always refuses, even under Validated. Supported targets refuse Pending and allow only the force-eligibility decision under Validated. Domain validity, budgets, immutable member binding and runtime custody remain outside this policy. + +Request presence uses any Some in its authoritative per-track snapshot vector, including Some(empty), disabled and zero fields. No field-effect evaluation or fallback to a draft/other member appears. Its target uses the consumed Request workflow: configured Some takes Unsupported precedence, then material Some maps ParticleMaterial, otherwise the existing native carrier branch maps NativeCarrier. The private cfg(test) bypass maps only to Validated, defaults false in ordinary literals and cannot grant Unsupported capability. Non-test compilation uses only the production constant. No public flag, environment selector, serialization field or mutable evidence registry is added. + +SceneUi readiness validates/freezes the canonical draft, then invokes the same policy with production Pending; it does not infer intent from the Retained display. Empty ordinary draft remains legacy None semantics, while invalid canonical drafts fail before policy. A retained authored document with an empty draft does not contaminate a new independent setup. Read-only retained projections and viewport/lab refusal remain unchanged. + +Visible run controls derive target from effective SetupWorkflow and material mode, so Accepted Session remains Unsupported even if a retained material preference is true. Private Preview start recomputes its consumed Preview/material target and rechecks before freeze/output; start_configured uses static Unsupported before provider/output preparation. The AI entry also uses static Unsupported rather than inheriting Preview capability. Existing configured/native publish/rerun paths still validate the actual immutable Request before their output boundaries; native saved rerun remains independent of current SceneUi/draft. No recording/core/per-track dispatch owner changes in this slice. + +## Error ordering note + +The initial source applied the authored particle single-track guard even when configured Some made the effective target Unsupported, causing a safe but misleading particle-track refusal. The repaired source derives the effective target and applies that cardinality guard only to ParticleMaterial. A new pure configured+material+two-track assertion requires Unsupported; the helper path was re-inspected and re-admitted. This resolves the message-order finding. Canonical document/member validation appropriately retains precedence over policy errors; a successful policy must never bypass it. + +The Pending copy now accurately says native interaction acceptance is pending, preserving the gate after the two tiny Worker results. An existing pure test still expected the former “validation is pending” substring: its first execution failed at that stale assertion, not eligibility behavior. `/tmp/rheon-ui-force-policy-exact4.log` is preserved. The owner changed only that assertion to exact shared `PENDING` error equality, leaving all helpers and subsequent binding checks unchanged; the affected fourth and unexecuted fifth exact paths were re-inspected/re-admitted after list4. No numerical case was rerun. + +Initial strict Clippy then identified `SceneUi::draft` as unused after direct-field centralization. Rather than suppressing the lint, readiness now calls `self.draft().freeze()` and reads `self.draft().objects`. The accessor is exactly `&self.forces`, with no state, lock or transformation; this is behavior-equivalent canonical-draft access. The source change was independently re-read, final static gates passed, and only the two accessor-dependent pure tests were repeated successfully. The original lint failure remains preserved as `clippy.log`. + +## Complete pure-path admission + +These five fully qualified names were inspected and admitted only for individually exact execution after enumeration: + +- `force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner` +- `force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets` +- `force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display` +- `force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members` +- `scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch` + +The new helpers construct literal disabled-zero or empty snapshots, scalar Requests and an intentionally incomplete configured revision sentinel. Supported preflight performs only geometry/buffer arithmetic; configured authored input is refused by policy before its invalid sentinel could reach provider/model preparation. SceneUi/default fixture calls validate/freeze, bind metadata and context projection only. Existing member tests use literal serde/hash/metadata validation and no recorder/readers/files. No test calls positive start/rerun, Worker, numerical constructors, history capture or SQLite. + +The UI refusal helper constructs App default with worker/history None, paints only `run_controls` into a Context and captures the possible SetupAction as a boolean. It physically presses/releases the disabled Simulate widget but never calls `dispatch_setup_action`, so even a guard regression cannot launch work in this helper. Named help/status widgets add only UI state. App drop has no worker to cancel/join. The test does not compose full setup/provider/AI/viewport owners. + +The decision table checks production Pending explicitly, no-metadata passage under both states, supported pending/validated outcomes and non-bypassable Unsupported. Adapter checks cover Some(empty), disabled-zero fields, configured precedence over material, invalid canonical draft and retained-versus-draft isolation. No numerical or native runtime claim follows from these pure paths. + +## Remaining gates + +Keep production Pending and the existing private test bypass mechanism unchanged until the separately reviewed enabling increment; neither pure tests nor the two prior tiny Worker cases supply native launch/cancellation/gesture acceptance. Future Unknown owners need explicit policy mapping review rather than inheriting native eligibility. Preserve runtime artifacts and prior native/focus/timeline evidence separately. A copied transfer manifest, if later supplied, is a separate evidence receipt; it is not claimed here. + +## Final reviewed source identity and receipt + +| File | SHA-256 | +| --- | --- | +| `src/bin/desktop/force_run_policy.rs` | `086d4ee8c18ff30f2165b8622a53d649bd9eb8166bbbbcb82dde3a0eb2427cc2` | +| `src/bin/desktop/force_run_policy_tests.rs` | `a518b47b71cb2aff5d7ce46169918c60d534c00ffccdd0ee5c90abcebbdcdebd` | +| `src/bin/desktop/scene_ui.rs` | `73c16415819c5215a797615f5893222c2ec96eebe2bc8d100b95d346bed7d208` | +| `src/bin/rheon-desktop.rs` | `2c614eb7ea5920ee450ffcb344555304146869b332d8f9fe50f8e7a7703f763c` | +| `src/bin/desktop/force_runtime_ui_tests.rs` | `1d242eaed1b416e3dfb2577d5610c4e0d6d0ca5040f80293681ae184090cebb9` | + +All log paths in this receipt begin `/tmp/rheon-ui-force-policy-`. Read-only receipt of final `list5.log`: 204 tests, zero benchmarks. `exact1.log`/`exact2.log` supply the unchanged pure decision-table and Request-target passes. `exact4-copy-fixed.log` supplies the legacy Request/member resolver pass after the copy assertion repair. Final `accessor-exact1.log` and `accessor-exact2.log` supply Scene readiness and disabled-run UI passes after the accessor-only source repair. Each accepted log records one pass, zero failures, 203 filtered out. Earlier `exact3.log`/`exact5.log` also passed before that accessor change; they are superseded for those two final-source assertions. This is five distinct pure cases, with narrowly justified affected repeats, not a broad suite or repeated numerical/SQLite execution. + +Final `check2.log` passed in 1.15 s and strict production `clippy2.log` in 4.73 s. The owner confirmed final `fmt2.log` exit zero; fmt2/diffcheck logs are empty. Final hash reads match the table above. The preserved stale-copy assertion failure and unused-accessor lint failure are not hidden or described as passes. The reviewer executed no tests/builds/native actions. The source changes and receipts leave production Pending, maintain Unsupported non-bypass and preserve the separately sealed runtime/native evidence boundaries. diff --git a/src/bin/desktop/force_run_policy.rs b/src/bin/desktop/force_run_policy.rs new file mode 100644 index 00000000..88cd5c19 --- /dev/null +++ b/src/bin/desktop/force_run_policy.rs @@ -0,0 +1,36 @@ +//! Desktop force eligibility only. No execution, persistence or field evaluation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Target { + NativeCarrier, + ParticleMaterial, + Unsupported, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Qualification { + Pending, + Validated, +} +// Remains closed until the exact native acceptance is returned and reviewed. +pub const PRODUCTION_QUALIFICATION: Qualification = Qualification::Pending; +pub const PENDING: &str = + "Authored forces cannot run yet: native interaction acceptance is pending"; +pub const UNSUPPORTED: &str = "Authored forces are unsupported for this workflow (configured/session/provider/free-surface/AI)"; + +/// Presence means authored metadata, including an empty, disabled or zero scene. +/// Qualification cannot grant capability to an unsupported execution owner. +pub fn check( + target: Target, + authored: bool, + qualification: Qualification, +) -> Result<(), &'static str> { + if !authored { + return Ok(()); + } + if target == Target::Unsupported { + return Err(UNSUPPORTED); + } + if qualification != Qualification::Validated { + return Err(PENDING); + } + Ok(()) +} diff --git a/src/bin/desktop/force_run_policy_tests.rs b/src/bin/desktop/force_run_policy_tests.rs new file mode 100644 index 00000000..d563c0be --- /dev/null +++ b/src/bin/desktop/force_run_policy_tests.rs @@ -0,0 +1,166 @@ +//! Pure target/metadata preflight only; no Worker, solver, SQLite or cleanup. +use super::*; +use force_run_policy::{ + PENDING, PRODUCTION_QUALIFICATION, Qualification, Target, UNSUPPORTED, check, +}; +fn fields() -> rheon::ForceSnapshot { + rheon::ForceScene { + version: 1, + objects: vec![rheon::ForceObject { + id: rheon::SceneObjectId(1), + name: "Disabled zero field".into(), + enabled: false, + transform: Default::default(), + units: rheon::ForceUnits::Acceleration, + kind: rheon::ForceKind::Directional { vector: [0.0; 3] }, + support: rheon::ForceSupport::Global, + time: Default::default(), + }], + } + .freeze() + .unwrap() +} +fn request() -> Request { + Request { + force_fields: Vec::new(), + force_validation: false, + collision_mesh: None, + size: 2, + steps: 1, + tight: false, + threads: 1, + methods: vec![PressureImplementation::JacobiPcgV1], + emitter: None, + observe: false, + history_budget_mib: 16, + preview_resolution: history::PreviewResolution::Edge(2), + domain_side: 1.0, + requested_dt: 1e-6, + material: None, + material_color: [0; 3], + recording_limits: rheon::recording::RecordingLimits { + max_database_bytes: 65536, + max_record_bytes: 16384, + max_records: 2, + }, + recording_directory: "unused-no-output".into(), + saved_set: None, + configured_set: None, + prior_run_ids: vec![], + manifest_receipt: None, + progress_gate: None, + failure_gate: None, + } +} +fn unsupported_request() -> ConfiguredRequest { + // Deliberately incomplete metadata sentinel: target refusal must happen before + // configured provider/model preparation. No fixture builder or filesystem. + ConfiguredRequest { + revision: run_sets::configured::ConfiguredRunSetRevision { + schema_version: 1, + kind: run_sets::configured::ConfiguredSetKind::ConfiguredAcceptedSessionSet, + set_id: "unused".into(), + parent_sha256: None, + baseline_member: "unused".into(), + members: vec![], + }, + project_root: "unused".into(), + runtime: None, + } +} +#[test] +fn force_policy_qualification_never_grants_unsupported_owner() { + assert_eq!(PRODUCTION_QUALIFICATION, Qualification::Pending); + for qualification in [Qualification::Pending, Qualification::Validated] { + assert_eq!( + check(Target::Unsupported, true, qualification), + Err(UNSUPPORTED) + ); + for target in [ + Target::NativeCarrier, + Target::ParticleMaterial, + Target::Unsupported, + ] { + assert_eq!(check(target, false, qualification), Ok(())); + } + } + for target in [Target::NativeCarrier, Target::ParticleMaterial] { + assert_eq!(check(target, true, PRODUCTION_QUALIFICATION), Err(PENDING)); + assert_eq!(check(target, true, Qualification::Validated), Ok(())); + } +} +#[test] +fn force_policy_request_and_setup_resolve_effective_targets() { + assert_eq!( + SetupWorkflow::Preview.force_target(false), + Target::NativeCarrier + ); + assert_eq!( + SetupWorkflow::Preview.force_target(true), + Target::ParticleMaterial + ); + for material in [false, true] { + assert_eq!( + SetupWorkflow::AcceptedSession.force_target(material), + Target::Unsupported + ); + } + for snapshot in [fields(), rheon::ForceScene::default().freeze().unwrap()] { + let mut r = request(); + r.force_fields = vec![Some(snapshot)]; + assert_eq!(r.force_target(), Target::NativeCarrier); + assert_eq!(validate_request(&r), Err(PENDING.to_owned())); + r.force_validation = true; + assert!(validate_request(&r).is_ok()); + r.material = Some(Default::default()); + assert_eq!(r.force_target(), Target::ParticleMaterial); + assert!(validate_request(&r).is_ok()); + r.configured_set = Some(unsupported_request()); + assert_eq!( + r.force_target(), + Target::Unsupported, + "configured takes precedence over material" + ); + assert_eq!(validate_request(&r), Err(UNSUPPORTED.to_owned())); + r.force_validation = false; + assert_eq!(validate_request(&r), Err(UNSUPPORTED.to_owned())); + r.methods = PressureImplementation::ALL.to_vec(); + r.force_fields.push(r.force_fields[0].clone()); + assert_eq!( + validate_request(&r), + Err(UNSUPPORTED.to_owned()), + "configured target owns refusal even with multiple material tracks" + ); + } +} +#[test] +fn force_policy_scene_readiness_uses_valid_draft_not_retained_display() { + let fields = fields(); + let draft = scene_ui::SceneUi::geometry_fixture(fields.scene().clone(), false); + for target in [Target::NativeCarrier, Target::ParticleMaterial] { + assert_eq!(draft.launch_readiness(target), Err(PENDING.to_owned())); + } + assert_eq!( + draft.launch_readiness(Target::Unsupported), + Err(UNSUPPORTED.to_owned()) + ); + let mut invalid = fields.scene().clone(); + invalid.objects[0].support = rheon::ForceSupport::Sphere { + radius: 0.0, + edge_fraction: 0.0, + }; + let invalid = scene_ui::SceneUi::geometry_fixture(invalid, false); + assert!( + invalid + .launch_readiness(Target::NativeCarrier) + .unwrap_err() + .contains("Invalid authored force") + ); + let mut empty = scene_ui::SceneUi::geometry_fixture(Default::default(), false); + empty.begin_run(false); + empty.bind_retained_fields(0, Some(&fields)).unwrap(); + assert!(!empty.display_scene().objects.is_empty()); + assert!(empty.draft().objects.is_empty()); + assert_eq!(empty.launch_readiness(Target::Unsupported), Ok(())); + assert_eq!(empty.launch_readiness(Target::NativeCarrier), Ok(())); +} diff --git a/src/bin/desktop/force_runtime_ui_tests.rs b/src/bin/desktop/force_runtime_ui_tests.rs index 76001555..b5a6681a 100644 --- a/src/bin/desktop/force_runtime_ui_tests.rs +++ b/src/bin/desktop/force_runtime_ui_tests.rs @@ -255,10 +255,9 @@ fn force_request_track_resolver_gates_before_work_and_binds_saved_members() { let fields = scene().freeze().unwrap(); let mut r = request(); r.force_fields = vec![Some(fields.clone())]; - assert!( - validate_request(&r) - .unwrap_err() - .contains("validation is pending") + assert_eq!( + validate_request(&r), + Err(force_run_policy::PENDING.to_owned()) ); r.force_validation = true; assert!(validate_request(&r).is_ok()); diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index 9634c4d0..bbc3a7a9 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -268,12 +268,16 @@ impl SceneUi { Some(*id) == self.model_id() || self.display_scene().objects.iter().any(|o| o.id == *id) }); } - pub fn launch_readiness(&self) -> Result<(), String> { - if self.draft().objects.is_empty() { - Ok(()) - } else { - Err("Authored forces cannot run yet: runtime and archive integration is pending. Remove authored forces to run the existing setup.".into()) - } + pub fn launch_readiness(&self, target: super::force_run_policy::Target) -> Result<(), String> { + self.draft() + .freeze() + .map_err(|error| format!("Invalid authored force: {error}"))?; + super::force_run_policy::check( + target, + !self.draft().objects.is_empty(), + super::force_run_policy::PRODUCTION_QUALIFICATION, + ) + .map_err(str::to_owned) } fn validate(&mut self) { self.error = self.forces.freeze().err().map(|e| { @@ -712,7 +716,11 @@ mod tests { let (output, _) = frame(&ctx, &mut scene, width, vec![]); click(&ctx, &mut scene, width, &output, "Enabled", false); assert!(!scene.forces.objects[2].enabled); - assert!(scene.launch_readiness().is_err()); + assert!( + scene + .launch_readiness(super::super::force_run_policy::Target::NativeCarrier) + .is_err() + ); let (output, _) = frame(&ctx, &mut scene, width, vec![]); click(&ctx, &mut scene, width, &output, "Hide scene", false); assert!(!scene.open); @@ -748,7 +756,11 @@ mod tests { scene.validate(); assert!(scene.error.is_some()); assert!(scene.draft().freeze().is_err()); - assert!(scene.launch_readiness().is_err()); + assert!( + scene + .launch_readiness(super::super::force_run_policy::Target::NativeCarrier) + .is_err() + ); assert_eq!(snapshot.scene().objects[0].support, ForceSupport::Global); let (output, _) = frame(&ctx, &mut scene, 900.0, vec![]); assert!(output.shapes.iter().any(|s|matches!(&s.shape,egui::Shape::Text(t) if t.galley.text().contains("Invalid authored force")))); @@ -842,7 +854,11 @@ mod tests { } assert!(app.worker.is_none() && app.history.is_none() && app.running_request.is_none()); app.scene.forces.objects[0].enabled = false; - assert!(app.scene.launch_readiness().is_err()); + assert!( + app.scene + .launch_readiness(super::super::force_run_policy::Target::NativeCarrier) + .is_err() + ); } #[test] fn scene_draft_timeline_roundtrip_preserves_retained_index_without_history() { diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index bcecf736..130431ee 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -52,6 +52,11 @@ pub mod run_sets; pub mod saved_comparison; use rheon::recording::comparison as native_comparison; use saved_comparison as comparison; +#[path = "desktop/force_run_policy.rs"] +mod force_run_policy; +#[cfg(test)] +#[path = "desktop/force_run_policy_tests.rs"] +mod force_run_policy_tests; #[cfg(test)] #[path = "desktop/force_runtime_integration_tests.rs"] mod force_runtime_integration_tests; @@ -132,6 +137,9 @@ struct ConfiguredRequest { runtime: Option<(Arc, accepted_session::model_process::ChildSlot)>, } impl Request { + fn force_target(&self) -> force_run_policy::Target { + self.setup_workflow().force_target(self.material.is_some()) + } fn fields_for_track(&self, track: usize) -> Option<&rheon::ForceSnapshot> { self.force_fields.get(track).and_then(Option::as_ref) } @@ -279,12 +287,13 @@ fn validate_request(request: &Request) -> Result<(), String> { rheon::recording::encode_force_metadata(fields).map_err(|e| e.to_string())?; } let authored = request.force_fields.iter().any(Option::is_some); - if authored && request.material.is_some() && request.methods.len() != 1 { + let target = request.force_target(); + if authored + && target == force_run_policy::Target::ParticleMaterial + && request.methods.len() != 1 + { return Err("Authored particle material requires exactly one material track".into()); } - if authored && request.configured_set.is_some() { - return Err("Authored forces are unsupported for configured/session/provider/free-surface execution".into()); - } if let Some(set) = &request.saved_set { set.validate().map_err(|e| e.to_string())?; if set.members.len() != request.methods.len() { @@ -303,15 +312,14 @@ fn validate_request(request: &Request) -> Result<(), String> { } } #[cfg(test)] - let force_admitted = request.force_validation; + let qualification = if request.force_validation { + force_run_policy::Qualification::Validated + } else { + force_run_policy::PRODUCTION_QUALIFICATION + }; #[cfg(not(test))] - let force_admitted = false; - if authored && !force_admitted { - return Err( - "Authored forces cannot run yet: bounded desktop runtime/archive validation is pending" - .into(), - ); - } + let qualification = force_run_policy::PRODUCTION_QUALIFICATION; + force_run_policy::check(target, authored, qualification).map_err(str::to_owned)?; if let Some(configured) = &request.configured_set { configured.revision.validate().map_err(|e| e.to_string())?; @@ -1148,6 +1156,13 @@ enum SetupWorkflow { AcceptedSession, } impl SetupWorkflow { + fn force_target(self, material: bool) -> force_run_policy::Target { + match self { + Self::AcceptedSession => force_run_policy::Target::Unsupported, + Self::Preview if material => force_run_policy::Target::ParticleMaterial, + Self::Preview => force_run_policy::Target::NativeCarrier, + } + } fn label(self) -> &'static str { match self { Self::Preview => "Simulation preview", @@ -1646,19 +1661,23 @@ impl App { ui.small(format!("Calculating step {step} · {elapsed:.3} s")); } } else { + let readiness = self + .scene + .launch_readiness(self.setup_workflow.force_target(self.material_mode)); + let can_launch = !self.closing && readiness.is_ok(); let launch = match self.setup_workflow { SetupWorkflow::Preview => ui .add_enabled( - !self.closing && self.scene.launch_readiness().is_ok(), + can_launch, egui::Button::new("Simulate") .min_size(egui::vec2(ui.available_width(), 32.0)), ) .clicked(), - SetupWorkflow::AcceptedSession => self - .configured_controls - .launch_button(ui, !self.closing && self.scene.launch_readiness().is_ok()), + SetupWorkflow::AcceptedSession => { + self.configured_controls.launch_button(ui, can_launch) + } }; - if let Err(error) = self.scene.launch_readiness() { + if let Err(error) = readiness { ui.colored_label(egui::Color32::YELLOW, "Authored-force runs unavailable") .on_hover_text(error); } @@ -1727,7 +1746,8 @@ impl App { Ok(limits) } fn start_configured(&mut self) -> Result<(), String> { - self.scene.launch_readiness()?; + self.scene + .launch_readiness(force_run_policy::Target::Unsupported)?; if self.worker.is_some() { return Err("Finish the active worker first".into()); } @@ -1915,7 +1935,10 @@ impl App { Ok((self.size, Some(config))) } fn start(&mut self, all: bool) { - if let Err(error) = self.scene.launch_readiness() { + if let Err(error) = self + .scene + .launch_readiness(SetupWorkflow::Preview.force_target(self.material_mode)) + { self.status = error; return; } @@ -2458,7 +2481,7 @@ impl App { ui.small("Fixed actual dt; a shortened step retains its archive and reports unsupported saved intent."); if ui.button("Compare both pressure solvers").clicked() {self.start(true);} } - if ui.add_enabled(self.worker.is_none() && self.scene.launch_readiness().is_ok(), egui::Button::new("Compare Rheon / trained AI")).clicked() { + if ui.add_enabled(self.worker.is_none() && self.scene.launch_readiness(force_run_policy::Target::Unsupported).is_ok(), egui::Button::new("Compare Rheon / trained AI")).clicked() { let mut comparison = ai_comparison::ComparisonApp::new(String::new(),std::env::current_dir().unwrap_or_default().join(".generated/ai-comparison")); comparison.set_steps(self.steps.clamp(1,512)); self.ai_comparison = Some(comparison); From 37ac711a4b7134f628c7a71d54f62056d71ca5f3 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 00:53:34 +0000 Subject: [PATCH 20/25] fix(ui): place desktop entry and custody helpers before test modules --- .../ci-item-order-20261021/manifest.json | 56 +++++++++++++++++++ .../rheon-ui-ci-book-9099b15-annotations.json | 1 + .../rheon-ui-ci-current-jobs.json | 4 ++ ...eon-ui-ci-desktop-1b37774-annotations.json | 1 + .../rheon-ui-ci-inventory-diagnosis.json | 41 ++++++++++++++ .../rheon-ui-ci-item-order-clippy-tests.log | 2 + .../rheon-ui-ci-item-order-fmt.log | 0 .../rheon-ui-ci-local-admission.log | 22 ++++++++ .../rheon-ui-ci-local-tests-clippy.log | 37 ++++++++++++ docs/plans/ui-refinement/execution-ledger.md | 18 ++++++ .../reports/ci-item-order-checkpoint.md | 55 ++++++++++++++++++ .../reports/ci-item-order-source-review.md | 20 +++++++ src/bin/desktop/inspector.rs | 50 ++++++++--------- src/bin/rheon-desktop.rs | 10 ++-- 14 files changed, 287 insertions(+), 30 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-book-9099b15-annotations.json create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-current-jobs.json create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-desktop-1b37774-annotations.json create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-inventory-diagnosis.json create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-clippy-tests.log create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-admission.log create mode 100644 docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-tests-clippy.log create mode 100644 docs/plans/ui-refinement/reports/ci-item-order-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/ci-item-order-source-review.md diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/manifest.json b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/manifest.json new file mode 100644 index 00000000..ca85dcc2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/manifest.json @@ -0,0 +1,56 @@ +{ + "base": "9099b150e8b80cd750db08276c51e180be4e61f6", + "fetched_main": "7516a8e5d328c6dc8eda8ea577cb03f20b2e343a", + "fetched_main_tree": "fef62baaeec871ff1d7d7083f73ab843a9cf93df", + "tests_executed": false, + "production_qualification": "Pending", + "admission_modified": false, + "ci_raw_log_downloads": "Forbidden; stopped; annotations contain only exit codes", + "sources": { + "src/bin/rheon-desktop.rs": "dfc472b78c46e83b45668a8d99bb82f721fcc771ddc584cdc8fdaf82272003ff", + "src/bin/desktop/inspector.rs": "7a6582faf9bfff755d7fade624f6b11cca06813c75310fba25ccbfabff1d2aae" + }, + "files": [ + { + "file": "rheon-ui-ci-book-9099b15-annotations.json", + "bytes": 838, + "sha256": "25c4e1d7a31d8d8aee5d04183eb81b897e342e82b3c59ed5b64b3991ec54f2f2" + }, + { + "file": "rheon-ui-ci-current-jobs.json", + "bytes": 470, + "sha256": "8af77b3671e77847358404b9561b222b3f0d6018f1643ec6042b883d6ddddf1f" + }, + { + "file": "rheon-ui-ci-desktop-1b37774-annotations.json", + "bytes": 842, + "sha256": "cdc77942d715d2463df966c3a5e38716c68af18cb9579f498f788ffddd4d6e23" + }, + { + "file": "rheon-ui-ci-inventory-diagnosis.json", + "bytes": 1410, + "sha256": "4bb9dc5e05926f90ba1fce3b4ae826bfa6e83d01c965c14655c5eda7852649f1" + }, + { + "file": "rheon-ui-ci-item-order-clippy-tests.log", + "bytes": 131, + "sha256": "b483aab364793644787ac9d4a26b0f9eba24c3c16592f68e6d0e5925f7d9ee80" + }, + { + "file": "rheon-ui-ci-item-order-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-ci-local-admission.log", + "bytes": 1646, + "sha256": "dc3454e148e2b9908b5c35158a4b0db5fbee703d55e815f331f089a0e9f091e5" + }, + { + "file": "rheon-ui-ci-local-tests-clippy.log", + "bytes": 1505, + "sha256": "6bceabdd4d7a6bd58502e964a36f309c181cc00ebc9797492b6d90d04870a3c3" + } + ], + "source_review_sha256": "b83657790b5110a78ed34b8aa41675026f609871ffd786613ba49f4977f627a8" +} diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-book-9099b15-annotations.json b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-book-9099b15-annotations.json new file mode 100644 index 00000000..36d9de89 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-book-9099b15-annotations.json @@ -0,0 +1 @@ +[{"path":".github","blob_href":"https://github.com/MrScripty/Rheon/blob/9099b150e8b80cd750db08276c51e180be4e61f6/.github","start_line":2,"start_column":null,"end_line":2,"end_column":null,"annotation_level":"warning","title":"","message":"Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/","raw_details":""},{"path":".github","blob_href":"https://github.com/MrScripty/Rheon/blob/9099b150e8b80cd750db08276c51e180be4e61f6/.github","start_line":11,"start_column":null,"end_line":11,"end_column":null,"annotation_level":"failure","title":"","message":"Process completed with exit code 1.","raw_details":""}] \ No newline at end of file diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-current-jobs.json b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-current-jobs.json new file mode 100644 index 00000000..1629465e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-current-jobs.json @@ -0,0 +1,4 @@ +{"conclusion":null,"id":118972926919,"name":"executable-contracts","status":"in_progress","steps":[]} +{"conclusion":"failure","id":118972927185,"name":"Feature contracts (desktop)","status":"completed","steps":["Strict lints for selected features"]} +{"conclusion":null,"id":118972927210,"name":"Feature contracts (core-only)","status":"in_progress","steps":[]} +{"conclusion":null,"id":118972927323,"name":"Feature contracts (default)","status":"in_progress","steps":[]} diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-desktop-1b37774-annotations.json b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-desktop-1b37774-annotations.json new file mode 100644 index 00000000..81ef4190 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-desktop-1b37774-annotations.json @@ -0,0 +1 @@ +[{"path":".github","blob_href":"https://github.com/MrScripty/Rheon/blob/1b377742e2dd2df0cc90c99c3ad735bfd480caad/.github","start_line":2,"start_column":null,"end_line":2,"end_column":null,"annotation_level":"warning","title":"","message":"Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/","raw_details":""},{"path":".github","blob_href":"https://github.com/MrScripty/Rheon/blob/1b377742e2dd2df0cc90c99c3ad735bfd480caad/.github","start_line":585,"start_column":null,"end_line":585,"end_column":null,"annotation_level":"failure","title":"","message":"Process completed with exit code 101.","raw_details":""}] \ No newline at end of file diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-inventory-diagnosis.json b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-inventory-diagnosis.json new file mode 100644 index 00000000..c4660b1c --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-inventory-diagnosis.json @@ -0,0 +1,41 @@ +{ + "head": "9099b150e8b80cd750db08276c51e180be4e61f6", + "historical_inventory_paths": 124, + "actual_conservative_paths": 136, + "added": [ + "src/bin/desktop/force_run_policy.rs", + "src/bin/desktop/force_run_policy_tests.rs", + "src/bin/desktop/force_runtime_integration_tests.rs", + "src/bin/desktop/force_runtime_ui_tests.rs", + "src/bin/desktop/native_presentation_harness.rs", + "src/bin/desktop/scene_handles.rs", + "src/bin/desktop/scene_ui.rs", + "src/bin/desktop/scene_view.rs", + "src/bin/desktop/setup_ui_tests.rs", + "src/bin/desktop/timeline_ui_tests.rs", + "src/force_field.rs", + "src/recording/force_fields.rs" + ], + "removed": [], + "changed_pinned": [ + "Cargo.toml", + "src/bin/desktop/carrier_execution.rs", + "src/bin/desktop/comparison.rs", + "src/bin/desktop/configured_controls.rs", + "src/bin/desktop/configured_worker_tests.rs", + "src/bin/desktop/inspector.rs", + "src/bin/desktop/run_sets.rs", + "src/bin/desktop/run_sets/source.rs", + "src/bin/desktop/settings.rs", + "src/bin/desktop/viewport.rs", + "src/bin/rheon-desktop.rs", + "src/forces.rs", + "src/lib.rs", + "src/particle_fluid.rs", + "src/recording.rs", + "src/recording/carrier.rs", + "src/recording/material.rs", + "src/simulation.rs" + ], + "method": "read-only conservative set/hash comparison; not native qualification; does not bypass ancestry gate" +} diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-clippy-tests.log b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-clippy-tests.log new file mode 100644 index 00000000..f89c927b --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-clippy-tests.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 27.13s diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-fmt.log b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-item-order-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-admission.log b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-admission.log new file mode 100644 index 00000000..39b810d0 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-admission.log @@ -0,0 +1,22 @@ +fatal: ambiguous argument 'f75cd66c6dc4167ad1ec3febb72e6fb2aa6b4d0b^{tree}': unknown revision or path not in the working tree. +Use '--' to separate paths from revisions, like this: +'git [...] -- [...]' +Traceback (most recent call last): + File "/workspace/Rheon-ui-authoring/docs/education/check_native_admission.py", line 22, in + sys.exit(main()) + ^^^^^^ + File "/workspace/Rheon-ui-authoring/docs/education/check_native_admission.py", line 8, in main + sources = source_core(ROOT) + ^^^^^^^^^^^^^^^^^ + File "/workspace/Rheon-ui-authoring/docs/education/aligned_strain_packet.py", line 142, in source_core + require(git(repo, 'rev-parse', PRIMARY_SOURCE + '^{tree}') == PRIMARY_TREE, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/Rheon-ui-authoring/docs/education/aligned_strain_packet.py", line 60, in git + return subprocess.check_output(['git', *args], cwd=repo, text=True).strip() + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/lib/python3.12/subprocess.py", line 466, in check_output + return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/opt/codex/runtimes/codex-primary-runtime/dependencies/python/lib/python3.12/subprocess.py", line 571, in run + raise CalledProcessError(retcode, process.args, +subprocess.CalledProcessError: Command '['git', 'rev-parse', 'f75cd66c6dc4167ad1ec3febb72e6fb2aa6b4d0b^{tree}']' returned non-zero exit status 128. diff --git a/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-tests-clippy.log b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-tests-clippy.log new file mode 100644 index 00000000..a90bd3a7 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/ci-item-order-20261021/rheon-ui-ci-local-tests-clippy.log @@ -0,0 +1,37 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) +error: items after a test module + --> src/bin/rheon-desktop.rs:2828:1 + | +2828 | mod tests { + | ^^^^^^^^^ +... +3880 | fn main() -> eframe::Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#items_after_test_module + = note: `-D clippy::items-after-test-module` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::items_after_test_module)]` + = help: move the items to before the test module was defined + +error: items after a test module + --> src/bin/desktop/inspector.rs:848:1 + | + 848 | mod tests { + | ^^^^^^^^^ +... +1166 | / pub(super) fn bind_force_fields( +1167 | | run: rheon::recording::RecordingRun, +1168 | | fields: Option<&ForceSnapshot>, +1169 | | ) -> Result { + | |___________________________________________________^ +... +1175 | / pub(super) fn verify_force_fields( +1176 | | run: &rheon::recording::RecordingRun, +1177 | | fields: Option<&ForceSnapshot>, +1178 | | ) -> Result<(), String> { + | |_______________________^ + | + = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.92.0/index.html#items_after_test_module + = help: move the items to before the test module was defined + +error: could not compile `rheon` (bin "rheon-desktop" test) due to 2 previous errors diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 27982746..150eafba 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -588,3 +588,21 @@ SQLite, Worker or native execution in this policy slice. OriginalM2a settings ha Gate enablement is a later reviewed increment after native prerequisites, with private bypass removal and separately bounded candidate validation. No M1/fullforce or accessibility/cancellation acceptance inferred from these scalar/Context tests. + + +## CI diagnosis and item ordering —2026-10-21 + +Fetched main7516a8e/treefef62baa; preserved tested3b and published9099 source/evidence. +Hosted desktop failures are strictlint-beforetests; rawlog downloads Forbidden and +stopped, annotations onlyexit101. Compile-only desktop--tests strictClippy reproduces +2items_after_test_module: test-harnessmain and2Inspectorhelpers. Literal unchanged +block relocations repair lint; same strict compileonly command passes27.13s; fmt/diff +pass. No broad/exact testbody or numerical/native/SQLite workload executed. + +Bookbuild fails reviewed-sourceadmission step, notnumerics. Existing124path inventory +has12added/18changed vs136current conservativepaths. Backendmain126path admission +is a separate sourceclosure and cannotqualifyUI. Local fullscript insteadstops on +shallow missingimmutableprimaryrevision; preserve traceback and distinguish from +unavailable hostedstderr. No hashrefresh/gateweaken/receiptcopy. Exact diagnosis, +source review and logs underreports/ci-item-order-checkpoint.md and corresponding +evidence. ProductionPending/fullCI/book/nativeforceacceptance remainopen. diff --git a/docs/plans/ui-refinement/reports/ci-item-order-checkpoint.md b/docs/plans/ui-refinement/reports/ci-item-order-checkpoint.md new file mode 100644 index 00000000..b5a4f5d1 --- /dev/null +++ b/docs/plans/ui-refinement/reports/ci-item-order-checkpoint.md @@ -0,0 +1,55 @@ +# PR146 CI diagnosis and item-order repair + +## Verified subject and observations + +Fetched main7516a8e5d328c6dc8eda8ea577cb03f20b2e343a / tree +fef62baaeec871ff1d7d7083f73ab843a9cf93df without merging/rebasing or changing the +UI source subject9099b150e8b80cd750db08276c51e180be4e61f6. PR146 remains a draft +against main. PR145/backend source and admissions have distinct preserved subjects. +The3b sealed Worker cases already passed once; no new numerical test was run here. + +Hosted job118964260530 (1b377742) and current118972927185 (9099b15) failed +**Strict lints for selected features**, before desktop contract test execution. +Their annotations give exit101, not compiler details. The log-download routes +returned network Forbidden; downloads were stopped. Do not present local output as +a retrieved hosted compiler log. No workflow rerun or gate weakening was requested. + +Local compile-only reproduction on9099b15: +`cargo clippy --locked --offline --features desktop --bin rheon-desktop --tests -- -D warnings` +failed with two `items_after_test_module` diagnostics: the cfg(test) native +presentation main after the desktop tests; Inspector bind_force_fields and +verify_force_fields after its tests. The repair literally relocates those unchanged +blocks before their respective test modules. No lint suppression, function-body, +visibility, cfg, dispatch or custody change. The same compile-only strict command +then passed in27.13s; formatting and diff checks pass. No test bodies, Worker, +SQLite or native session executed. Full hosted CI acceptance remains pending. + +## Book admission remains correctly refused + +Current bookbuild job118972927128 stopped at **Check reviewed native admission +before expensive qualification**, exit1. No numerical comparison ran in that job. +Raw stderr is unavailable because its log download was Forbidden. Read-only source +comparison independently confirms a stale inventory: branch9099b15 retains the +historical124-path review/inventory; its conservative source closure is136paths, +12added/18changed/none removed. Merged backend main has its own126-path admission, +which does not cover the later desktop closure. This branch has not merged that +admission; preserving it alone would still leave added/changed UI sources unadmitted. + +The local full admission script cannot reach the inventory guard because this +checkout is shallow and lacks immutable primaryf75cd66c6dc4167ad1ec3febb72e6fb2aa6b4d0b. +Its preserved traceback is an environment limitation, not a reproduction of hosted +stderr. The inventory diagnosis separately compares the existing selector's whole +set and hashes without asserting ancestry/qualification. No inventory, review record, +selector, threshold, workflow or source pins changed. Never refresh these hashes +automatically or copy backend receipts as UI acceptance. A later coordinated source +integration needs explicit complete desktop-closure review/admission and fresh exact +committed native/rational/publication evidence under that owner's authorization. + +## Evidence and remaining boundaries + +[Exact logs, metadata, source seals and hash manifest](../evidence/ci-item-order-20261021/manifest.json) +retain the initial lint failure, repaired strict result and shallow-history refusal. +[Independent relocation review](ci-item-order-source-review.md) covers source semantics. +The policy stays Pending. Native gesture/force-launch/cancellation acceptance, +full CI and book source admission remain open. No new execution budget, security, +network configuration, merge or cleanup is part of this repair. diff --git a/docs/plans/ui-refinement/reports/ci-item-order-source-review.md b/docs/plans/ui-refinement/reports/ci-item-order-source-review.md new file mode 100644 index 00000000..c495cbb6 --- /dev/null +++ b/docs/plans/ui-refinement/reports/ci-item-order-source-review.md @@ -0,0 +1,20 @@ +# Independent CI item-order source review + +Suitable for a separate minimal lint repair over `9099b150e8b80cd750db08276c51e180be4e61f6`. No material source blocker remains. The reviewer read source/logs and wrote only this report; no test, build, numerical/model/native workload, admission check or inventory mutation was executed. + +The diff relocates the existing cfg(test) native-presentation main before main's test module and the two Inspector force-binding helpers before Inspector's test module. Independent exact-block comparison against HEAD established that each moved block is byte-identical and occurs once, and that every other nonblank source line is identical after removing that block. Cfg attributes, visibility, function bodies, error behavior, snapshot/header binding, production main and test contents are preserved. No lint suppression or force qualification change is introduced. Existing pure helper evidence continues to apply; no numerical test rerun is warranted for literal item ordering. + +| Final reviewed file | SHA-256 | +| --- | --- | +| `src/bin/rheon-desktop.rs` | `dfc472b78c46e83b45668a8d99bb82f721fcc771ddc584cdc8fdaf82272003ff` | +| `src/bin/desktop/inspector.rs` | `7a6582faf9bfff755d7fade624f6b11cca06813c75310fba25ccbfabff1d2aae` | + +`/tmp/rheon-ui-ci-local-tests-clippy.log` reproduces two `items_after_test_module` errors with `-D warnings`, identifying exactly those blocks. After relocation, `/tmp/rheon-ui-ci-item-order-clippy-tests.log` records compile-only strict desktop-bin/test Clippy success in 27.13 s. The command is `cargo clippy --locked --offline --features desktop --bin rheon-desktop --tests -- -D warnings`; it compiles test targets without executing bodies. The owner confirms fmt/diff checks passed; reviewer read-only `git diff --check` also passed. This establishes the targeted local lint repair, not a completed hosted all-target CI run. + +Hosted metadata for current 9099 reports `Feature contracts (desktop)` failing at “Strict lints for selected features”; the Rust workflow runs that before contract tests. The earlier 1b37774 metadata likewise identified strict lints. Raw hosted log downloads returned Forbidden and were stopped; annotations provide exit codes, not diagnostic text. Do not describe the local Clippy text as downloaded CI stderr or claim all CI jobs now pass. + +The book failure is separate. The workflow's early reviewed-native-admission step invokes `docs/education/check_native_admission.py`. The actual local attempt stopped earlier while resolving missing immutable primary revision `f75cd66c6dc4167ad1ec3febb72e6fb2aa6b4d0b` in the shallow checkout. It did not reach the later inventory refusal, so it is not an exact reproduction of hosted admission stderr. An independent read-only reconstruction of the conservative path/hash comparison nevertheless confirms the UI branch's historical integration inventory has 124 paths versus 136 actual paths: twelve additions, eighteen changed pinned files, no removals. Fetched main `7516a8e5d328c6dc8eda8ea577cb03f20b2e343a` (tree `fef62baaeec871ff1d7d7083f73ab843a9cf93df`) has a 126-path integration inventory; it is not a complete inventory for this UI branch. + +No admission file, receipt, workflow gate or inventory hash was changed. Copying main's inventory or refreshing hashes automatically would not qualify the additional UI closure. Any later admission work must explicitly review the final committed source and obtain its required fresh qualification under separate authority. These literal moves also change file bytes, so semantic equivalence is not an exception to source identity checks. Production force qualification remains Pending; parked scientific/native acceptance is not supplied by this CI diagnosis. + +The copied `evidence/ci-item-order-20261021/manifest.json` was independently checked against both final sources and all eight packet/raw file pairs by hash and size. It preserves the pre-fix local failure, post-fix compile-only pass, hosted metadata, inventory diagnosis and shallow-history failure. No test execution is claimed by the packet or this review. Hosted CI rerun and the separately blocked admission qualification remain outstanding. diff --git a/src/bin/desktop/inspector.rs b/src/bin/desktop/inspector.rs index 26041c77..754bfbaa 100644 --- a/src/bin/desktop/inspector.rs +++ b/src/bin/desktop/inspector.rs @@ -844,6 +844,31 @@ fn csv(m: &RecordedMaterial, id: usize, run_id: u64) -> String { text } +pub(super) fn bind_force_fields( + run: rheon::recording::RecordingRun, + fields: Option<&ForceSnapshot>, +) -> Result { + match fields { + Some(fields) => run.with_force_fields(fields).map_err(|e| e.to_string()), + None => Ok(run), + } +} +pub(super) fn verify_force_fields( + run: &rheon::recording::RecordingRun, + fields: Option<&ForceSnapshot>, +) -> Result<(), String> { + use sha2::{Digest, Sha256}; + let expected = fields + .map(rheon::recording::encode_force_metadata) + .transpose() + .map_err(|e| e.to_string())? + .map(|bytes| <[u8; 32]>::from(Sha256::digest(bytes))); + if run.force_fields_digest().map_err(|e| e.to_string())? != expected { + return Err("Recording force snapshot does not match immutable request".into()); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -1162,28 +1187,3 @@ mod tests { } } } - -pub(super) fn bind_force_fields( - run: rheon::recording::RecordingRun, - fields: Option<&ForceSnapshot>, -) -> Result { - match fields { - Some(fields) => run.with_force_fields(fields).map_err(|e| e.to_string()), - None => Ok(run), - } -} -pub(super) fn verify_force_fields( - run: &rheon::recording::RecordingRun, - fields: Option<&ForceSnapshot>, -) -> Result<(), String> { - use sha2::{Digest, Sha256}; - let expected = fields - .map(rheon::recording::encode_force_metadata) - .transpose() - .map_err(|e| e.to_string())? - .map(|bytes| <[u8; 32]>::from(Sha256::digest(bytes))); - if run.force_fields_digest().map_err(|e| e.to_string())? != expected { - return Err("Recording force snapshot does not match immutable request".into()); - } - Ok(()) -} diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index 130431ee..7a5119a0 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -2824,6 +2824,11 @@ fn main() -> eframe::Result { }), ) } +#[cfg(test)] +fn main() -> eframe::Result { + native_presentation_harness::run_native_presentation() +} + #[cfg(test)] mod tests { use super::*; @@ -3875,8 +3880,3 @@ mod tests { assert!(app.status.starts_with("Cancelled")); } } - -#[cfg(test)] -fn main() -> eframe::Result { - native_presentation_harness::run_native_presentation() -} From da5c68891a7c627007ccb7136c9c9cd6b42fee26 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 01:06:49 +0000 Subject: [PATCH 21/25] fix(ui): retain force property disclosure and editor identity across previews --- .../manifest.json | 83 +++++++ ...n-ui-gate-readiness-requirements-review.md | 126 +++++++++++ ...i-scene-stability-authoring-regression.log | 8 + .../rheon-ui-scene-stability-check.log | 2 + .../rheon-ui-scene-stability-clippy.log | 2 + .../rheon-ui-scene-stability-diff.log | 0 ...on-ui-scene-stability-disclosure-after.log | 8 + ...n-ui-scene-stability-disclosure-before.log | 21 ++ .../rheon-ui-scene-stability-fmt.log | 0 .../rheon-ui-scene-stability-focus-after.log | 8 + .../rheon-ui-scene-stability-focus-before.log | 23 ++ .../rheon-ui-scene-stability-list-after.log | 211 ++++++++++++++++++ .../rheon-ui-scene-stability-list-before.log | 211 ++++++++++++++++++ .../rheon-ui-scene-stability-plan-check.log | 1 + docs/plans/ui-refinement/execution-ledger.md | 19 ++ docs/plans/ui-refinement/plan.md | 19 +- .../force-gate-next-candidate-requirements.md | 73 ++++++ .../scene-properties-stability-checkpoint.md | 80 +++++++ ...cene-properties-stability-source-review.md | 55 +++++ src/bin/desktop/scene_ui.rs | 150 ++++++++++++- 20 files changed, 1096 insertions(+), 4 deletions(-) create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/manifest.json create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-gate-readiness-requirements-review.md create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-authoring-regression.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-check.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-clippy.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-diff.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-after.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-before.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-fmt.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-after.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-before.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-after.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-before.log create mode 100644 docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-plan-check.log create mode 100644 docs/plans/ui-refinement/reports/force-gate-next-candidate-requirements.md create mode 100644 docs/plans/ui-refinement/reports/scene-properties-stability-checkpoint.md create mode 100644 docs/plans/ui-refinement/reports/scene-properties-stability-source-review.md diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/manifest.json b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/manifest.json new file mode 100644 index 00000000..24ee9621 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/manifest.json @@ -0,0 +1,83 @@ +{ + "base_commit": "37ac711a4b7134f628c7a71d54f62056d71ca5f3", + "source": "src/bin/desktop/scene_ui.rs", + "source_sha256": "46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390", + "enumerated_total": 206, + "exact_pure_after_cases": [ + "scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel", + "scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus", + "scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context" + ], + "before_failures_preserved": 2, + "numerical_execution": false, + "native_execution_by_owner": false, + "production_qualification": "Pending", + "files": [ + { + "file": "rheon-ui-gate-readiness-requirements-review.md", + "bytes": 8054, + "sha256": "caf279a3cdd779671c3cb800ea9ec3252f0186a116a06bbe6e3bebe71064fc3a" + }, + { + "file": "rheon-ui-scene-stability-authoring-regression.log", + "bytes": 399, + "sha256": "291acbf05100c55ed609432f4874ff88694e68c4eb62469e44653866895f67aa" + }, + { + "file": "rheon-ui-scene-stability-check.log", + "bytes": 130, + "sha256": "5adfe47eb2a9b5b9ef51e4c77d25ade237255a8d7e514d584469a5c7619dee43" + }, + { + "file": "rheon-ui-scene-stability-clippy.log", + "bytes": 130, + "sha256": "8c0e6e0907e6712eabdfcf565baacf868b526e4e3c55103788e4939398bc2251" + }, + { + "file": "rheon-ui-scene-stability-diff.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-scene-stability-disclosure-after.log", + "bytes": 401, + "sha256": "7920e6fcf08b502ba9a6d3ad8730101200a8384da47ed764fda2ab8a3f289f8e" + }, + { + "file": "rheon-ui-scene-stability-disclosure-before.log", + "bytes": 918, + "sha256": "d7782a15a521fbcfbe400e3538d0c1ab6c6d496f9fb8d1583937a28e5dc89d61" + }, + { + "file": "rheon-ui-scene-stability-fmt.log", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "rheon-ui-scene-stability-focus-after.log", + "bytes": 393, + "sha256": "a025239c1e7e2565425f5603b5bd2620af8d9abc0e2193ffd2837b3a5293b2e2" + }, + { + "file": "rheon-ui-scene-stability-focus-before.log", + "bytes": 970, + "sha256": "59c843f1d1298f8f64c8036542d7d5e29d572899c8c6f23bee3cb4f6433af09d" + }, + { + "file": "rheon-ui-scene-stability-list-after.log", + "bytes": 21605, + "sha256": "37f111fa1e4e49623b13d3d31943161d2bd4fa15af48c381c47489ca9aeb27b1" + }, + { + "file": "rheon-ui-scene-stability-list-before.log", + "bytes": 21606, + "sha256": "9918d22dadd297444ca45eb2b2fbab55ee7a0d8747b64129e1d6374b8db6c877" + }, + { + "file": "rheon-ui-scene-stability-plan-check.log", + "bytes": 83, + "sha256": "3f347682033eb0d17ff36cc1b37f9fbd2b1d7b4db01a4b9f596a9e499b2594a5" + } + ], + "source_review_sha256": "a3df4594d6d9daaf2167919c48ce0b184b3f2acb4a6e07717c37666600769904" +} diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-gate-readiness-requirements-review.md b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-gate-readiness-requirements-review.md new file mode 100644 index 00000000..177590b2 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-gate-readiness-requirements-review.md @@ -0,0 +1,126 @@ +# Independent next force-gate readiness requirements + +Disposition: keep production Pending now. A small enabling candidate is suitable +after the concrete prerequisites below; broader feature/scientific acceptance is +not required or implied. This report authorizes no source edit, build, test, +native/numerical action, publication or authentication retry. + +Reviewed current policy, canonical plan, typed-policy/source review, property +stability review, native handoffs, CI/admission checkpoint and independent Worker +result review at HEAD `37ac711a4b7134f628c7a71d54f62056d71ca5f3`, with the property +repair still locally modified. No tests/builds or raw native replay were performed. +Only this temporary report is written. + +## Evidence carried forward with its limits + +The independently reviewed two Worker/SQLite passes belong to sealed revision +3b3fccea and executable af2da153: one eight-cell native Jacobi track and one +64-particle material track, one interval each, dispatch/effect/custody and cold +readback. Do not attribute those runs automatically to later policy/property code. +Owner review may relate unchanged runtime paths to later presentation/policy +deltas; no numerical rerun follows from writing this contract. + +The task's accepted native 1b evidence is representative: Radial creation, +tree/viewport selection, thin outline, X Move/Rotate, Sphere Size, Escape/camera +isolation and close. It is not all force types/axes/supports, full focus, live +Cancel or scientific qualification. There is no need to invent an all-axis/type +qualification matrix as a prerequisite to this bounded eligibility change. + +The new property repair changes persistent editor/disclosure identity. Its two +pure regressions and native repaired behavior require their own final-source +receipts; old 1b evidence does not accept that change. + +## Prerequisites before the enabling candidate + +1. **Exact source/CI ownership:** the repository owner admits the complete current + desktop closure and resolves required CI for that exact committed subject. + Use the authoritative existing source-admission process; do not automatically + refresh inventory hashes, copy backend receipts, alter selectors/thresholds or + weaken a refused guard. Older main/backend admission and local check output do + not constitute hosted CI/source acceptance for these added desktop paths. + Missing owner admission/CI remains a specific prerequisite, not permission to + run scientific/book jobs or repair credentials in this task. +2. **Finish the property repair checkpoint:** final individually exact disclosure + and invalid-radius-focus regression receipts, relevant static gates and + independent source review identify the committed repair. Preserve the baseline + failures and earlier passing receipts rather than replacing their history. +3. **Targeted exact-repair native acceptance:** the existing native owner verifies + that committed repair, build/revision and recorded window/scale. Reproduce the + affected path: open Transform/Support; enter handle preview, apply and cancel + without collapsing the disclosures; type an invalid sphere radius and correct + it without clicking to recover focus. Keep visible validation and ordinary + camera/selection ownership. Use the failing/relevant native size/scale already + observed, expanding only for a new finding. This remains idle authoring: no + Run, model import or second native owner/session. + +The source owner currently cannot publish because GH_TOKEN is expired. Preserve +the local checkpoint and hand exact source/receipt identity to the repository +owner for coordination. Do not retry authentication/network, change credentials +or present the missing publication/CI result as successful. This does not block +read-only review or already authorized local repair work. + +## Small enabling candidate after those prerequisites + +Scope: the existing policy constant, Request qualification adapter, SceneUi/main +copy/readiness projection and directly affected pure/fixture tests. Change +production Qualification to Validated and remove `force_validation`, its cfg(test) +branch and all fixture assignments. Do not add a replacement flag, environment +switch, capability registry, evaluator, worker, recorder/schema or rerun format. +Retain the current three-target policy; qualification never grants Unsupported. + +Remove the unconditional Scene banner/tooltip claiming all authored runs or +runtime/archive integration are unavailable. Prefer the existing effective-target +launch/readiness owner for contextual refusal rather than teaching the scene +drawer a second workflow policy. Keep invalid-draft errors, unsupported-target +reasons, units/limitations and legacy unknown retained metadata visible. Do not +replace real unsupported lab/session messages with blanket availability. + +Independently inspect helpers, enumerate, then execute only individually exact +**pure** cases covering: + +- Supported native and particle Request preflight passes using ordinary production + qualification, without the bypass. Invalid documents, snapshot/member mismatch, + budgets and single-material-track validation still refuse normally. +- Configured takes precedence over material; Accepted Session, AI and existing + provider/transport/free-surface/lab owners remain explicitly Unsupported. + Empty/disabled/zero authored metadata does not bypass those refusals. Legacy + None stays legacy; saved member force/None intent stays independent of draft. +- Effective setup/native/material mapping and canonical Scene readiness agree. + Supported valid drafts produce one Run intent when the real control is + pressed/released; unsupported/invalid cases produce no Run intent and a reason. + Tests must stop before dispatch and inspect complete helpers for I/O/provider + initialization as well as Worker calls. A disabled button alone is insufficient + evidence for the private/direct start guards. +- No stale blanket-unavailable copy appears for an eligible supported draft; + unsupported and invalid feedback remains. Previously frozen/Retained metadata + is not replaced or modified by readiness/UI intent projection. + +No positive App start/rerun, Worker, simulation, recording/SQLite or filesystem +fixture belongs in these tests. Mechanical ignored-test literal updates compile +but do not authorize rerunning those bodies. Complete affected lint/type/build/ +format checks and independent source review; repository owner then admits the +**final candidate** closure and required CI. Its new revision is not admitted by +reusing the pre-candidate hashes. Keep the candidate isolated and reversible. + +## Subsequent enabled App validation, separately owned + +After the candidate exists, the current native owner validates a separately +bounded actual force-enabled App launch on that exact candidate, under a concrete +execution contract coordinated by the parent. This cannot be a prerequisite to +writing the code that first enables launch. It is a prerequisite to claiming the +enabled application works, publishing an accepted enablement, or wider rollout. + +The later contract should identify the tiny UI setup, force/target, single launch, +existing supervisor/output bounds and immutable snapshot/Retained observations. +Validate the actual UI launch path and truthful supported/unsupported feedback; +do not treat the earlier direct Request Worker tests as an App interaction. +No numerical/native run or retry is authorized by this review. Genuine active +Cancel/join remains separately scoped rather than inferred from a completed tiny +run or idle close/Escape. + +Material still has no serialized FrozenSpec/full cold-rerun API; retained Request +plus cold force association is the available narrower evidence. Full material +cold rerun, mixed-member runtime, emitter/collision combinations, M1 gaps, +screen-reader/high-DPI breadth, all-axis/type gestures and scientific/legacy +equivalence remain explicitly unclaimed. Preserve all prior captures, archives, +raw logs, source and branches; rollback only the enabling increment if needed. diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-authoring-regression.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-authoring-regression.log new file mode 100644 index 00000000..2132595e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-authoring-regression.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.19s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.10s + diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-check.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-check.log new file mode 100644 index 00000000..31a5f785 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-check.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.21s diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-clippy.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-clippy.log new file mode 100644 index 00000000..5b2cca97 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-clippy.log @@ -0,0 +1,2 @@ + Checking rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 6.84s diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-diff.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-diff.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-after.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-after.log new file mode 100644 index 00000000..82123b91 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-after.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.04s + diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-before.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-before.log new file mode 100644 index 00000000..4fb62abf --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-disclosure-before.log @@ -0,0 +1,21 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel ... FAILED + +failures: + +---- scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel stdout ---- + +thread 'scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel' (65063) panicked at src/bin/desktop/scene_ui.rs:558:28: +missing visible Position X +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.01s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-fmt.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-fmt.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-after.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-after.log new file mode 100644 index 00000000..8ec8e559 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-after.log @@ -0,0 +1,8 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.04s + diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-before.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-before.log new file mode 100644 index 00000000..262a0730 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-focus-before.log @@ -0,0 +1,23 @@ + Finished `test` profile [unoptimized + debuginfo] target(s) in 0.18s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) + +running 1 test +test scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus ... FAILED + +failures: + +---- scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus stdout ---- + +thread 'scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus' (65072) panicked at src/bin/desktop/scene_ui.rs:849:13: +assertion `left == right` failed: error banner must retain radius focus at 900 + left: None + right: Some(28FD) +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 205 filtered out; finished in 0.02s + +error: test failed, to rerun pass `--bin rheon-desktop` diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-after.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-after.log new file mode 100644 index 00000000..bb47906a --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-after.log @@ -0,0 +1,211 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 9.74s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +206 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-before.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-before.log new file mode 100644 index 00000000..a7d5b568 --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-list-before.log @@ -0,0 +1,211 @@ + Compiling rheon v0.1.0 (/workspace/Rheon-ui-authoring) + Finished `test` profile [unoptimized + debuginfo] target(s) in 10.87s + Running unittests src/bin/rheon-desktop.rs (/workspace/.rheon-tools/target/debug/deps/rheon_desktop-51d01e4509203a9f) +accepted_session::accepted_provider::tests::arbitrary_supported_shape_uses_bounded_zero_guess_and_rejects_diagnostic_mode: test +accepted_session::accepted_provider::tests::invalid_pin_metadata_and_scratch_cap_never_start_a_child: test +accepted_session::accepted_provider::tests::production_bridge_full_and_correction_qualify_two_intervals_with_exact_native_fields: test +accepted_session::accepted_provider::tests::real_inflight_cancellation_and_deadline_keep_publication_receipts_quota_and_join_child: test +accepted_session::accepted_provider::tests::rejected_candidate_preserves_same_request_native_pressure_as_next_prior: test +accepted_session::accepted_provider::tests::runtime_failure_retains_dispatch_fact_and_fresh_owner_retries_accepted_native_history: test +accepted_session::accepted_provider::tests::support_decline_is_inert_and_preserves_context_and_native_fallback: test +accepted_session::accepted_provider::tests::typed_invalid_payload_digest_and_runtime_context_decline_survive_coarse_fallback: test +accepted_session::configured_track::tests::disabled_and_newtonian_zero_remain_distinct_and_native_only_start_is_inert: test +accepted_session::configured_track::tests::executor_package_prefix_physics_and_caps_fail_before_child_and_preserve_constructor: test +accepted_session::configured_track::tests::frozen_track_full_and_correction_follow_real_native_prefix_and_exact_native_fields: test +accepted_session::configured_track::tests::host_rejected_candidate_keeps_runtime_and_native_fallback_as_next_accepted_prior: test +accepted_session::configured_track::tests::host_shortened_dt_support_declines_without_fake_dispatch_and_retains_native_step: test +accepted_session::configured_track::tests::independent_eight_cell_cpu_candidate_qualifies_nonzero_rhs_without_prior_at_phase_zero: test +accepted_session::configured_track::tests::missing_retired_or_occupied_runtime_never_silently_substitutes_native_configuration: test +accepted_session::configured_track::tests::real_inflight_cancellation_and_deadline_keep_accepted_receipt_fields: test +accepted_session::configured_track::tests::real_runtime_failure_and_invalid_output_retire_after_truthful_fallback_and_allow_explicit_fresh_owner: test +ai_comparison::adapter::tests::closed_records_and_unknown_contracts_fail: test +ai_comparison::adapter::tests::correction_and_selected_optional_prior_require_context: test +ai_comparison::adapter::tests::digest_and_length_drift_are_rejected: test +ai_comparison::adapter::tests::discovery_verifies_material_without_executing_and_accepts_unequal_counts: test +ai_comparison::adapter::tests::duplicate_keys_and_entry_limits_fail: test +ai_comparison::adapter::tests::escaping_and_aliased_symlink_inventory_is_rejected: test +ai_comparison::adapter::tests::physical_and_history_mismatches_are_unsupported: test +ai_comparison::adapter::tests::traversal_unmeasured_entrypoint_and_invalid_budgets_fail: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::accepted_session_write_contract::actual_session_blocked_write_cancel_and_deadline_preserve_the_recorded_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::actual_cpu_failures_and_quality_rejections_record_only_accepted_native_fallbacks: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::cold_configured_archive_child: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::consumed_request_cancellation_and_session_deadline_leave_only_the_durable_seed: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::direct_runtime_write_deadline_reports_dispatch_and_keeps_owner_until_drop: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::accepted_recording_contract_tests::entered_prediction_can_cancel_before_writer_dispatch_without_an_accepted_row: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_child_exit_preserves_typed_failure_and_accepted_native_fallback: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::actual_cpu_full_and_correction_packages_qualify_after_native_seed_with_exact_fields: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_before_dispatch_without_consuming_request: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::cooperative_poll_cancels_blocked_writer_after_initiation_and_joins_io: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::physical_support_declines_before_prediction_and_keeps_native_context: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_bad_candidate_falls_back_on_same_rhs_and_supplies_native_next_prior: test +ai_comparison::cpu_contract_tests::accepted_provider_contract_tests::real_inflight_session_cancellation_and_deadline_preserve_publication_and_retry_quota: test +ai_comparison::cpu_contract_tests::higher_density_does_not_widen_legacy_or_adapter_physical_support: test +ai_comparison::cpu_contract_tests::inert_preflight_result_survives_finished_worker_rendering: test +ai_comparison::cpu_contract_tests::legacy_records_still_match_the_actual_lab_v2_reader_contract: test +ai_comparison::cpu_contract_tests::stalled_input_write_times_out_and_owned_child_and_io_threads_drain: test +ai_comparison::cpu_contract_tests::support_preflight_is_inert_and_rejects_before_execution: test +ai_comparison::cpu_contract_tests::two_actual_packages_change_ports_and_output_role_without_host_changes: test +ai_comparison::diagnostic_view::tests::changed_grid_clears_physical_probe_and_native_labels_report_actual_failed_gate: test +ai_comparison::diagnostic_view::tests::programmatic_namespace_change_and_failed_reopen_clear_old_attempts: test +ai_comparison::diagnostic_view::tests::projection_cache_uses_exact_arc_retention_and_caches_unavailable_outcomes: test +ai_comparison::diagnostic_worker::tests::append_budget_failure_retains_reconciled_failed_prefix_and_explicit_unrecorded_tail: test +ai_comparison::diagnostic_worker::tests::cancel_in_native_warmup_closes_actual_zero_attempt_prefix: test +ai_comparison::diagnostic_worker::tests::cancellation_during_real_prediction_retains_dispatched_request_and_drains_child: test +ai_comparison::diagnostic_worker::tests::changed_executor_stamp_blocks_saved_execution_but_historical_cold_source_stays_readable: test +ai_comparison::diagnostic_worker::tests::cold_native_seed_matches_actual_prefix_in_closed_and_incomplete_archives: test +ai_comparison::diagnostic_worker::tests::cold_terminal_attempts_respect_actual_prefix_and_requested_horizon: test +ai_comparison::diagnostic_worker::tests::first_rejection_is_retained_without_frame_or_history_advance: test +ai_comparison::diagnostic_worker::tests::fresh_rerun_preserves_source_and_rechecks_counts_provider_and_frozen_spec: test +ai_comparison::diagnostic_worker::tests::saved_runtime_failures_preserve_frozen_intent_parent_and_no_child_execution: test +ai_comparison::diagnostic_worker::tests::teacher_records_rejected_candidates_but_completes_native_future_and_cold_metrics: test +ai_comparison::diagnostic_worker::tests::typed_headless_failure_returns_error_after_preserving_terminal_observation: test +ai_comparison::engine::tests::bad_ai_is_observed_without_contaminating_reference_or_fallback: test +ai_comparison::engine::tests::native_prefix_supplies_previous_solved_pressure_before_first_prediction: test +ai_comparison::engine::tests::zero_forcing_keeps_both_tracks_exact_and_cancellation_stops: test +ai_comparison::engine::typed_tests::cancelled_qualified_candidate_is_retained_without_committing_or_publishing_frame: test +ai_comparison::engine::typed_tests::independent_first_rejection_preserves_both_histories_and_equal_request_reference: test +ai_comparison::engine::typed_tests::independent_qualified_multistep_context_and_time_survive_cold_verification: test +ai_comparison::engine::typed_tests::malformed_output_is_explicit_unavailable_with_digest_and_no_advanced_frame: test +ai_comparison::engine::typed_tests::teacher_bias_never_changes_native_request_history_and_exact_clock_cold_verifies: test +ai_comparison::model_process::prediction_contract_tests::actual_error_codes_override_misleading_message_words_without_text_classification: test +ai_comparison::model_process::prediction_contract_tests::cancellation_after_child_observes_request_preserves_dispatch: test +ai_comparison::model_process::prediction_contract_tests::correction_overflow_is_invalid_output_with_the_actual_reply_digest: test +ai_comparison::model_process::prediction_contract_tests::invalid_wire_replies_keep_the_complete_line_digest_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::pinned_start_rejects_changed_binding_before_any_child_side_effect: test +ai_comparison::model_process::prediction_contract_tests::predispatch_support_context_cancellation_and_closed_input_have_typed_reasons: test +ai_comparison::model_process::prediction_contract_tests::reply_deadline_and_child_eof_are_runtime_failures_after_dispatch: test +ai_comparison::model_process::prediction_contract_tests::request_wire_budget_is_rejected_before_dispatch: test +ai_comparison::model_process::prediction_contract_tests::typed_success_preserves_the_actual_subprocess_and_legacy_result: test +ai_comparison::model_process::tests::protocol_is_closed_and_rejects_oversized_pressure: test +ai_comparison::saved_rerun_tests::actual_saved_rerun_button_never_renders_old_arrays_with_new_grid: test +ai_comparison::saved_rerun_tests::live_attempt_cache_reuses_published_arc_and_clears_on_absence_or_new_worker: test +ai_comparison::scene::tests::density_and_geometry_round_trip_without_widening_other_budgets: test +ai_comparison::scene::tests::rectangular_domain_has_one_cell_size_and_bounded_history: test +ai_comparison::view::tests::difference_is_signed_zero_centered_and_range_uses_all_cells: test +ai_comparison::view::tests::rectangular_world_preserves_equal_edges_and_picking: test +ai_comparison::worker::tests::configurable_cpu_adapter_cancels_after_predictions_and_restarts: test +ai_comparison::worker::tests::real_cpu_deployment_cancellation_drains_child_and_allows_restart: test +archive_view::tests::cold_open_real_egui_navigation_and_invalid_reload_clear_selected_identity: test +carrier_execution::tests::admitted_carrier_above_old_axis_limit_can_finish_its_thumbnail: test +collision_ui::tests::native_import_reject_remove_and_repeat_are_transactional: test +comparison_field_view::canvas_contract::common_color_normalization_handles_overflow_and_constant_ranges_explicitly: test +comparison_field_view::canvas_contract::native_canvas_fits_single_sample_axes_and_rejects_nonfinite_or_unrepresentable_extents: test +configured_comparison_tests::actual_egui_configured_page_compute_exports_and_edits_invalidate_both_linked_and_unverified_sources: test +configured_comparison_tests::actual_quality_fallback_and_invalid_payload_comparisons_keep_runtime_and_host_outcomes_distinct: test +configured_comparison_tests::full_and_correction_worker_outputs_compare_known_pressure_then_cold_linked_probe_and_inspector_csv: test +configured_comparison_tests::resealed_saved_artifacts_enforce_source_version_kind_and_exact_recomputation: test +configured_comparison_tests::worker_process_exits_before_fresh_comparison_reader_without_model_runtime: test +configured_controls::tests::actual_constructor_capture_preserves_raw_controls_for_both_native_methods: test +configured_controls::tests::changed_declarations_and_path_require_explicit_recapture_and_support_is_checked_inertly: test +configured_controls::tests::disabled_and_applied_zero_viscosity_freeze_distinct_stage_and_workspace_intent: test +configured_controls::tests::inert_inspection_pins_pair_and_freeze_uses_captured_bytes_without_reloading: test +configured_controls::tests::invalid_physics_regions_limits_and_raw_constructor_controls_fail_closed: test +configured_producer_exit_tests::worker_storage_tail_survives_producer_exit_then_model_absent_reader: test +configured_worker_tests::actual_paired_worker_records_full_and_correction_then_cold_rerun_preserves_originals: test +configured_worker_tests::actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel: test +configured_worker_tests::changed_host_refuses_configured_rerun_before_output_or_runtime: test +configured_worker_tests::configured_worker_cold_child: test +configured_worker_tests::constructor_capture_failure_is_terminal_before_model_start: test +configured_worker_tests::consumed_request_worker_cancel_retains_native_seed_and_unaccepted_runtime_terminal: test +emitter_controls::tests::direction_speed_and_zero_are_consistent: test +emitter_controls::tests::rendering_controls_retains_si_fields: test +engineering::tests::egui_controls_pick_masked_and_fluid_cells_and_keep_vectors_above_tiles: test +engineering::tests::probe_polygon_and_constant_legend_are_defined: test +engineering::tests::real_obstacle_projection_is_accepted_and_masked: test +engineering::tests::session_slice_matches_native_fields_and_refreshes_accepted_publications: test +engineering::tests::staggered_linear_velocity_reconstructs_at_physical_cell_centers: test +export_ui::tests::exports_refuse_overwrite_and_remove_only_failed_new_output: test +force_run_policy_tests::force_policy_qualification_never_grants_unsupported_owner: test +force_run_policy_tests::force_policy_request_and_setup_resolve_effective_targets: test +force_run_policy_tests::force_policy_scene_readiness_uses_valid_draft_not_retained_display: test +force_runtime_integration_tests::material_force_dispatch_records_immutable_scene: test +force_runtime_integration_tests::native_force_dispatch_records_and_cold_binds_v2: test +force_runtime_ui_tests::force_intent_legacy_v1_bytes_and_digest_remain_exact: test +force_runtime_ui_tests::force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize: test +force_runtime_ui_tests::force_intent_v2_freeze_roundtrip_and_mixed_member_differences_are_exact: test +force_runtime_ui_tests::force_recording_header_preflight_rejects_missing_wrong_or_double_binding_without_io: test +force_runtime_ui_tests::force_request_track_resolver_gates_before_work_and_binds_saved_members: test +force_runtime_ui_tests::force_retained_composition_resolves_once_before_tree_and_defers_track_intents: test +force_runtime_ui_tests::force_retained_track_projection_is_readonly_and_clears_same_id_selection: test +history::tests::explicit_preview_counts_density_and_serialization_are_independent: test +history::tests::finer_nondivisible_preview_has_no_holes_and_does_not_change_numerical_state: test +history::tests::long_comparisons_keep_every_step_with_a_bounded_preview_resolution: test +history::tests::occupancy_counts_numerical_cells_even_below_the_preview_threshold: test +history::tests::previews_above_128_are_real_and_long_timelines_obey_the_selected_budget: test +history::tests::scrubbing_does_not_follow_new_frames_and_rejects_out_of_order_publication: test +inspector::tests::carrier_field_selection_absence_real_egui_vti_export_and_metadata_errors: test +inspector::tests::historical_selection_survives_capture_reload_and_egui_export_uses_that_step: test +native_presentation_harness::synthetic_retained_history_timeline_and_disabled_modes_are_workerless: test +particle_view::tests::selected_recorded_points_follow_timestep_and_clear_when_run_disappears: test +scene_handles::tests::handles_cancellation_context_focus_pointer_and_camera_loss_never_hands_off: test +scene_handles::tests::handles_geometry_ray_translation_units_and_nonidentity_local_rotation: test +scene_handles::tests::handles_named_controls_fit_narrow_without_touching_authoring_values: test +scene_handles::tests::handles_pointer_invalid_support_release_restores_base_and_snapshot: test +scene_handles::tests::handles_pointer_release_commits_once_and_escape_consumes_until_release: test +scene_handles::tests::handles_transaction_invalid_final_position_never_commits_stale_preview: test +scene_ui::tests::scene_draft_timeline_roundtrip_preserves_retained_index_without_history: test +scene_ui::tests::scene_force_refusal_projects_disabled_run_without_dispatch: test +scene_ui::tests::scene_identity_context_removal_and_exhaustion_preserve_canonical_values: test +scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus: test +scene_ui::tests::scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation: test +scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context: test +scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel: test +scene_view::tests::geometry_depth_near_clip_and_degenerate_hits: test +scene_view::tests::geometry_force_glyphs_preserve_zero_direction_sign_and_support: test +scene_view::tests::geometry_two_sided_outline_welds_exact_seams_and_refuses_nonmanifold: test +scene_view::tests::scene_pick_identity_overlap_retained_and_unavailable_preview: test +scene_view::tests::scene_pointer_selects_same_identity_ignores_drag_and_modified_click: test +settings::tests::mass_density_units_use_independent_si_conversion: test +settings::tests::ui_material_help_is_tab_reachable_and_model_limits_remain_visible: test +settings::tests::ui_material_presets_and_open_menus_fit_narrow_forms: test +settings::tests::ui_material_selection_updates_only_its_existing_owner: test +settings::tests::ui_material_thermal_disclosures_preserve_values_and_si_editing: test +settings::tests::ui_precision_explicit_edits_keep_si_units_and_ranges: test +settings::tests::ui_precision_tab_and_focus_preserve_material_bits: test +setup_ui_tests::bounded_genuine_cancel_contract: test +setup_ui_tests::ui_setup_accepted_session_uses_carrier_inputs_without_changing_preview_mode: test +setup_ui_tests::ui_setup_expanded_forms_keep_text_inside_sidebar_width: test +setup_ui_tests::ui_setup_long_recording_path_and_pressure_controls_stay_within_form_width: test +setup_ui_tests::ui_setup_pointer_switch_keeps_one_launch_and_preserves_inputs: test +setup_ui_tests::ui_setup_running_summary_uses_frozen_physical_samples_and_timestep: test +setup_ui_tests::ui_setup_status_details_are_bounded_and_actions_survive_scroll_and_inspector: test +setup_ui_tests::ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_signal: test +setup_ui_tests::ui_setup_uninspected_adapter_does_not_emit_launch_intent: test +setup_ui_tests::ui_setup_viewport_caption_wraps_at_narrow_scaled_width: test +tests::archive_opening_frame_suppresses_the_previous_live_inspector: test +tests::automatic_threads_follow_hardware_and_manual_counts_stay_within_the_limit: test +tests::cancel_after_progress_joins_without_rows_and_allows_restart: test +tests::cancellation_before_publication_suppresses_already_finished_result: test +tests::cancelled_worker_never_publishes_rows: test +tests::collision_run_retains_geometry_and_reset_repeats_real_worker: test +tests::configured_carrier_density_freezes_independently_of_particle_mode_and_preview: test +tests::density_request_survives_domain_edits_and_does_not_follow_preview_count: test +tests::docked_inspector_cannot_overlap_viewport_at_supported_window_sizes: test +tests::dropping_app_cancels_and_joins_owned_worker: test +tests::live_emitter_frames_arrive_before_join_and_scrubbing_survives_background_work: test +tests::material_worker_accepts_and_captures_a_genuine_256_cube_preview: test +tests::material_worker_publishes_live_frames_and_retains_immutable_physical_scale: test +tests::repeated_start_cannot_replace_active_work: test +tests::saved_native_cold_rerun_process: test +tests::saved_native_rerun_after_producer_exit_avoids_all_source_ids: test +tests::saved_native_worker_closes_and_cold_opens_exact_two_member_set: test +tests::sidebar_sections_collapse_and_preserve_their_state_through_real_egui_input: test +tests::thousand_per_metre_zero_flow_cold_reruns_with_independent_preview_and_unchanged_archive: test +tests::worker_runs_selected_and_equal_input_comparison: test +timeline_ui_tests::timeline_controls_retain_transitions_and_physical_time: test +timeline_ui_tests::timeline_empty_idle_uses_content_height_after_sidebar: test +timeline_ui_tests::timeline_narrow_scaled_track_and_time_captions_stay_inside_clip: test +timeline_ui_tests::timeline_speed_edit_focus_bounds_validation_and_keyboard: test +timeline_ui_tests::timeline_track_selection_and_report_rows_fit_cold_and_changed_states: test +transport_view::tests::live_view_and_export_follow_api_publication_and_reset_identity: test +viewport::header_ui_tests::responsive_mode_tabs_keep_whole_labels_at_900_650_150_percent: test +viewport::tests::emitter_visual_strengths_follow_selected_mode_and_zero_disables_jets: test +viewport::tests::enabled_scene_input_hover_and_keyboard_focus_complete_without_nested_context_lock: test +viewport::tests::excessive_surface_is_refused_without_downsampling: test +viewport::tests::lab_navigation_preserves_the_live_camera_and_uses_its_own_framing: test +viewport::tests::native_touchpad_scroll_pans_orbits_and_pinches_through_the_viewport_input_path: test +viewport::tests::perspective_projection_and_camera_motion_are_finite_and_independent_of_history: test +viewport::tests::primary_drag_orbits_pans_and_zooms_without_alt: test +viewport::tests::voxel_surface_culls_internal_faces: test + +206 tests, 0 benchmarks diff --git a/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-plan-check.log b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-plan-check.log new file mode 100644 index 00000000..a38a7e1e --- /dev/null +++ b/docs/plans/ui-refinement/evidence/scene-properties-stability-20261021/rheon-ui-scene-stability-plan-check.log @@ -0,0 +1 @@ +Pinned structural plan validation passed; no standards-engine certificate claimed. diff --git a/docs/plans/ui-refinement/execution-ledger.md b/docs/plans/ui-refinement/execution-ledger.md index 150eafba..514a832a 100644 --- a/docs/plans/ui-refinement/execution-ledger.md +++ b/docs/plans/ui-refinement/execution-ledger.md @@ -606,3 +606,22 @@ shallow missingimmutableprimaryrevision; preserve traceback and distinguish from unavailable hostedstderr. No hashrefresh/gateweaken/receiptcopy. Exact diagnosis, source review and logs underreports/ci-item-order-checkpoint.md and corresponding evidence. ProductionPending/fullCI/book/nativeforceacceptance remainopen. + + +## Draft property disclosure and invalid-radius focus —2026-10-21 + +Parent-relayed native1b representativeRadial/XMoveRotate/SphereSize/treeviewport/ +outline/Escape/cameraisolation/closeexit0 passes; notallaxes/types. Native found +previewdisclosurecollapse and refocusrequiredafterinvalidradius; nativefocuscause +untraced. Two exact admitted Context-only regressions on37ac failedbeforefix: +missingvisiblePositionX(phaseunidentifiedbyhelper) and focusededitor->None after +radius0/errorframe at900. Preservebothfailurelogs. + +Minimal scene_ui productionhelper shares explicitperobjectUi identity between +normalDraft and disabledpreview; stable childautoIDs withstand precedingerrorrows. +Retainedscope/parser/units/validation/transaction/snapshot unchanged. After:2exact +newtests plus1exactexistingauthoringregression pass,205filtered each.206list-only; +check1.21s/strictClippywithtests6.84s/fmt/diff pass. No numerical/Worker/SQLite/model/ +filesystemfixture or native session. SourceSHA46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390. +New exact idle nativehandoff and quality85JPEG requirements are in checkpointreport; +repairnativepending, productionPending. Authenticationremainsblocked, noretry. diff --git a/docs/plans/ui-refinement/plan.md b/docs/plans/ui-refinement/plan.md index 3fa2e55e..7b1a83ee 100644 --- a/docs/plans/ui-refinement/plan.md +++ b/docs/plans/ui-refinement/plan.md @@ -156,8 +156,8 @@ Generated logs/screenshots/transfer packets remain outside Git. | S1 | Empty idle timeline fits <=70 logical px in900/1250px windows with the preceding280px sidebar reserved; selected-frame captions remain present and may naturally wrap/grow; actual Play/Pause/step/Live input keeps prior transitions | integration / isolated egui painting/input / automated | pending | Four exact probes pass: idle48px versus106px, focused speed editing and native-track/literal-report layouts. Native/configured/popup-keyboard coverage remains open | | S2 | Right panel collapses/reopens retaining selection; tree/viewport choose the same existing object; deleted/replaced/missing identities never edit another object | integration + user-workflow / inert UI then native / automated + manual | pending | Canonical authoring source and five exact UI probes pass; viewport picking/native remain pending | | S3 | A force is one canonical scene object in tree, properties, outline and handles; UI exposes only shared supported units/support/transform; unsupported backend blocks with a reason | integration / shared domain contract + inert UI / reviewed + automated | blocked | Canonical authoring source uses one ForceScene and checked IDs; dispatch/frozen intent source implemented and gated; two bounded3b Worker cases pass; native acceptance pending | -| S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | T4 reviewed geometry/transaction source and six exact inert tests pass; full Viewport gesture/native evidence pending | -| S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; prior426fd25 images do not cover this source | +| S4 | Handle capture prevents simultaneous camera/probe motion; Escape restores exact draft; release commits one validated unit-correct edit; active snapshots stay immutable | integration + user-workflow / inert UI then native / automated + manual | pending | T4 inert tests and parent-relayed native1b representative X/Sphere gestures pass; property-state repair native acceptance and broader axes/types remain pending | +| S5 |1250×820 and900×650 at100%/150%: usable viewport, no overlap/clipping, visible status/Cancel/errors, thin outline, keyboard alternatives and predictable drawer focus | user-workflow / actual native display / manual | blocked | No local display; parent-relayed native1b representative gestures/scale evidence is scoped; current property repair requires exact native acceptance | | S6 | Locked affected check, strict Clippy, formatting and individually selected exact UI tests pass; existing archives/source ownership unchanged | focused / pinned Rust1.92 / automated + source review | pending | T5 thirteen exact inert tests, locked build/check, strict production Clippy and independent source review pass; no archive workload or scientific certificate | The former M1 A4/A5 and live cancellation remain unsatisfied. UI-5 has a reviewed @@ -269,3 +269,18 @@ update its fixtures. Then validate actual force-enabled App launch on that exact isolated candidate under separately bounded native ownership. No live execution or gate change is implied by this plan. Rollback only the policy/adapters increment; preserve original UI/native/runtime evidence, archives and historical hash contracts. + + +## Property-state repair and enablement checkpoint + +Parent reports native1b representative Radial/XMoveRotate/SphereSize/tree/viewport/ +outline/Escape/cameraisolation/close passes. Property collapse and invalid-radius +refocus were observed there; native focus cause remains untraced. Current local +repair shares stable per-object Draft UI identity across normal/preview/error-row +transitions. Two new pure regressions reproduce failures before and pass after; +one exact existing authoring regression and static checks pass. Exact native repair +acceptance remains pending. [Native handoff](reports/scene-properties-stability-checkpoint.md) +and [next candidate requirements](reports/force-gate-next-candidate-requirements.md) +separate Pending preparation, complete source admission/CI, targeted native repair, +future narrow enabling diff/pure tests and separately authorized enabled App launch. +They do not unpark genuine cancellation or admit scientific workloads. diff --git a/docs/plans/ui-refinement/reports/force-gate-next-candidate-requirements.md b/docs/plans/ui-refinement/reports/force-gate-next-candidate-requirements.md new file mode 100644 index 00000000..c35aca42 --- /dev/null +++ b/docs/plans/ui-refinement/reports/force-gate-next-candidate-requirements.md @@ -0,0 +1,73 @@ +# Force gate: exact next candidate requirements + +This is a reviewed plan, not enablement or a new execution authorization. +PRODUCTION_QUALIFICATION stays Pending in the property-repair checkpoint. + +1. **Exact source and CI admission.** Repository/qualification owner admits the final + committed desktop closure against merged backend7516a8e's126-path admission. + Preserve backend appended review and historical v1/v2 bytes; independently review + the added/changed UI paths and complete conservative closure. No copied receipt, + automatic hash refresh, selector/gate weakening or assumption that semantic + relocation bypasses byte identity. Required hosted CI/admission/qualification + results must bind the actual candidate. Local shallow ancestry failure is not + proof of a hosted inventory result. This worker runs no scientific qualification. +2. **Property repair native acceptance.** Existing native owner tests the exact new + source using scene-properties-stability-checkpoint.md. Disclosure must stay open + through preview/apply/Escape; invalid0m radius must be corrected to0.4m without + refocus. Record actual window/scale/events/outcome and source/binary seals. + Prior1b evidence is representative X/Sphere only; no all-axis/type claim is needed + or inferred. New exact failure requires correction/review, not gate removal. +3. **Small source candidate only after prerequisites.** Change production qualification + to Validated and remove Request's private cfg(test) force_validation field/bypass + and all associated literal fields. Use the same policy for production/test paths. + Keep authored-presence Some semantics (including empty/disabled/zero), configured + precedence, unsupported owner refusals, canonical validation and immutable member + binding. No flag/env selector, fallback physics, new capability framework or + recording schema change. Preserve legacy None behavior and historical hashes. +4. **Truthful setup presentation.** Replace Scene's unconditional unavailable banner + and stale runtime-pending tooltip with state derived from the shared policy. + Supported readiness may emit an intent only for valid Draft input; configured, + provider/session/free-surface/AI remain explicitly unsupported. Active-worker and + lab safeguards remain. Retained document is read-only and never supplies a new + independent draft. Removing an obsolete warning must not hide actual errors. +5. **Candidate pure/source checks.** Inspect helpers, enumerate, then select individual + --exact UI-only cases. Update the existing decision table for actual production + qualification; prove supported native/material Request readiness, unsupported + precedence even with material/multiple tracks, Some(empty)/disabled-zero presence, + invalid/budget/member mismatch refusal and exact saved-member force/None custody. + Test setup/AI/private readiness and positive/negative enabled-button intent capture + WITHOUT dispatch_setup_action/start/Worker/output. Existing refusal-only test must + become a truthful intent test, not silently retain old Pending expectations. Strict + desktop-bin/test lint, compile, fmt, independent review and final source seals pass. + No numerical/SQLite suite is implicitly admitted by these source changes. +6. **Enabled App launch is a separate bounded validation step.** After reviewing the + candidate, native owner receives a concrete per-path contract and separate execution + authorization. Use the established one-native8-cell/one-step/max100iterations and + one64particle/one-step/max16preparations envelopes where appropriate, with exact + frozen legal inputs and fresh recording/capture roots. The earlier headless test + limits (30s/20CPU/512MiBAS/1MiBper-file+log) are evidence for those test children, + not assumed suitable limits or authorization for a GPU/native App process. The + native owner must return exact GUI-compatible existing process/session bounds + and failure-preserving disposition before execution; no new cgroup/mount/security + provisioning or silent budget expansion. Verify actual + App Request dispatch/progress/result, original frozen scene despite independent + draft edit, constructor+accepted force association, SQLite coldreadback and native + saved intent. Do not reuse test-bypass admission or silently launch another baseline. + Input/fixture/presentation differences require review before this step executes. + Temporary disk peak remains unknown; final8file8MiB is not an aggregate hardcap. +7. **Honest release boundaries and rollback.** Prior3b Worker passes establish those + tiny cases, not all force combinations, M1 or scientific parity. SGS/multitrack, + emitters/collisions, other supports/time windows retain their actual evidence gaps; + owner review must assess them rather than label them proven. Material has no + serialized cold-rerun API: retained typed Request+cold force association only. + Genuine interrupted Cancel remains separately parked/unaccepted; mock UI-state + checks are not worker cancellation. Archive build-unspecified strings require + external source/binary seals and cannot be called authenticated revision provenance. + If enabling validation fails, restore Pending in a new isolated commit and retain + all artifacts; no branch reset, output cleanup or receipt rewriting. + +No current credential change/retry, workload, publication or merge is authorized +by this checklist. Parent coordinates source admission, publication and native +execution through the existing owners. JPEGs must be actual revised native frames, +quality85 unless readability requires more, with exact state/revision labels and +confirmed attachment identities before delivery claims. diff --git a/docs/plans/ui-refinement/reports/scene-properties-stability-checkpoint.md b/docs/plans/ui-refinement/reports/scene-properties-stability-checkpoint.md new file mode 100644 index 00000000..15023046 --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-properties-stability-checkpoint.md @@ -0,0 +1,80 @@ +# Stable Draft force properties — source checkpoint and native handoff + +## Scoped evidence and observed defects + +Parent relays native exact1b377742 evidence: force creation, Radial tree/viewport +selection, thin outlines, representative X Move/Rotate, Sphere Size, Escape rollback, +camera isolation and normal close exit0 passed. This is ONLY representative X-axis +and Sphere coverage, not all axes/types, accessibility or force-enabled execution. +The same native session observed expanded properties collapsing during drag previews +and an invalid radius needing another focus click before correction. The native +focus cause was not traced. Preserve its earlier logs/images; this owner has no display. + +Over exact local parent37ac711, two independently admitted Context-only regressions +were compiled/listed individually (206total) then run with their fully qualified +names and --exact --test-threads=1. Both failed before the repair: disclosure lacked +visible Position X (helper panic does not identify the precise transaction phase); +invalid-radius correction specifically lost focused editor28FD on the next error +frame at width900. This confirms a local focus-loss transition, not every native cause. + +## Minimal repair + +Only scene_ui.rs production source changes. Normal Draft properties and the +mutually exclusive disabled drag-preview properties now share a per-object +UiBuilder::id scope. Its persistent ID uses the existing stable scene object ID +and property-parent namespace. Pinned egui Ui::new_child seeds auto-widget IDs +from parent allocation counters unless an explicit global Ui identity is supplied; +an error banner can add a row before the scroll area. A plain push_id stabilizes +collapsing headers but cannot independently stabilize those numeric auto-IDs. + +The explicit shared identity preserves disclosure and editor state across the +tested branches/row changes. Retained properties keep their distinct scope; +force values, parser/units/validation, preview transactions and immutable run +snapshots are unchanged. No forced focus request, error suppression or numeric +clamping is added. New scope is rendered once per frame, so it does not duplicate +an object widget ID in parallel trees. + +After repair, both exact regressions pass. Disclosure checks Transform and Support +through preview plus apply/cancel without draft/frozen mutation. Focus checks900 +and1250 widths: enter radius0, retain visible blocking error/focus, correct to0.4m +without clicking, preserve frozen0.25m. The existing exact pointer authoring/name/ +units/strength/enabled/hide/reopen/Retained-context regression also passes. All3 +have205filtered; no broad test selection, Worker, numerical/model/SQLite/filesystem +fixture or cleanup. Locked production check, strict desktop-bin/test Clippy, +formatting and diff checks pass. Before failures and all receipts are retained. + +## Exact native handoff — idle only + +Use the new exact checkpoint commit/tree, not1b or a synthetic disabled canvas. +Build production desktop using the established authorized native environment; +record source/binary/window/scale seals. Production force qualification is Pending. +No Simulate/Compare/Worker/model/run/cancel workload is needed for these checks. + +1. At1250x820/100% and900x650/150%, open Scene and create/select a Radial force. + Set Sphere support to0.25m and expand Transform and Support & activation. + Scroll to inspect fields when necessary; do not reduce fonts to fit. +2. On the selected force use representative X Move/Rotate and Sphere Size handles. + Inspect expanded property fields during valid previews and after release/apply. + Repeat with Escape cancellation: disclosure remains open, preview returns to the + exact starting values, and camera remains independent. This covers those gestures + only; unexercised axes/types remain unclaimed. +3. In the visible Radius numeric editor, enter0. The blocking invalid-force status + stays visible and runs remain unavailable. Without clicking or refocusing, select + the text and enter0.4. The same editor retains keyboard ownership; the error clears + and units remain metres. If it fails, preserve exact event order/focus and screenshots + rather than attributing it to the earlier source mechanism automatically. +4. Hide/reopen Scene; retain selection/values/disclosure. Close normally and record + exit status. Do not infer accessibility/tab-wide or genuine cancellation acceptance. + +Capture actual updated rendered JPEGs at quality85 (higher only for text readability), +label revision/state/window/scale and retain original evidence. Library attachment +identities may be reported only after confirmed upload; none is claimed here. + +## Boundaries + +[Pure/static logs and seals](../evidence/scene-properties-stability-20261021/manifest.json) +and [independent review](scene-properties-stability-source-review.md) establish local +source/UI evidence only.37ac transfer remains immutable; this later repair changes +SceneUi bytes and needs its own exact native/checkpoint and source-closure binding. +No authentication retry, publication, admission refresh, numerical re-execution, +M1 acceptance or production gate enablement is included. diff --git a/docs/plans/ui-refinement/reports/scene-properties-stability-source-review.md b/docs/plans/ui-refinement/reports/scene-properties-stability-source-review.md new file mode 100644 index 00000000..423585a6 --- /dev/null +++ b/docs/plans/ui-refinement/reports/scene-properties-stability-source-review.md @@ -0,0 +1,55 @@ +# Independent Scene properties stability source review + +Disposition: the minimal source repair and focused local egui evidence are acceptable for this checkpoint; no material source blocker found. This review does not accept native repaired behavior. + +## Scope and cause + +The production change is limited to the two Draft property-editor call sites and one shared `draft_properties` helper in `src/bin/desktop/scene_ui.rs`. Normal editing and a disabled drag preview now render the same object under one explicit persistent per-object UI ID. The branches are mutually exclusive, so the shared ID does not introduce duplicate simultaneous editors. Retained properties keep their separate existing identity. + +Before the repair, the normal editor used a `force-properties` per-object `push_id`, while the preview omitted that scope. This is a concrete source-level disclosure identity mismatch. Pinned egui 0.33.3 additionally seeds auto widget IDs from both the stable child ID and the parent's current auto-ID salt (`Ui::new_child`, ui.rs306–314). An ordinary `push_id` therefore does not by itself isolate numeric editor IDs from an inserted preceding error row. `UiBuilder::id` uses global scope and independently seeds the child IDs; its documented purpose is preserving widget state across UI-tree moves. The helper derives that explicit ID from the persistent parent/object identity and uses the same nested enabled-editor structure in both Draft branches. + +The local before tests reproduced the disappearance of a visible `Position X` field and loss of the radius editor's focus after invalid-radius feedback at width 900. The disclosure failure's generic helper panic does not identify the exact phase by itself. The source mismatch and auto-ID mechanism explain the targeted repair; no native event trace proves the native focus symptom's precise cause. + +## Invariants and helper admission + +`force_properties`, the shared precise quantity editor, units, numeric parsing, domain validation/error copy, canonical selection, launch policy and transaction commit/cancel code are unchanged. Preview still edits only its temporary candidate; a valid release revalidates and replaces canonical data through the existing transaction. Invalid radius remains visible as an error and refuses freezing; the repair does not clamp invalid values or suppress feedback. Prior frozen snapshots retain their owned data. + +Complete inspected paths: `sphere_scene`, `SceneUi::default/add/show`, `context/frame/text_rect/click/key/replace`, `draft_properties/force_properties/quantity`, `settings::quantity/precise_drag`, and `begin_edit/preview_edit/finish_edit/freeze_for_run` through `ForceScene::freeze` and `ForceObject::validate`. They only construct/validate/clone canonical metadata and process/paint egui UI. They do not construct App/Worker, call acceleration/evaluate, perform simulation/model work, recording, filesystem operations or cleanup. + +Only these individually exact test paths were admitted after list enumeration: + +- `scene_ui::tests::scene_properties_disclosure_survives_preview_apply_and_cancel` +- `scene_ui::tests::scene_invalid_radius_can_be_corrected_without_refocus` +- Existing affected regression: `scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context` (separately re-inspected and admitted). + +The first checks both Transform and Support disclosures through multiple preview frames and cancel/apply, canonical-draft isolation, applied values and a previously frozen support snapshot. The second uses actual pointer/keyboard UI at widths 900 and 1250, invalid radius feedback, focus identity, correction without another click, restored validity and frozen-snapshot isolation. The existing regression also exercises Add/name/units/strength/enabled/hide/reopen/context inputs; its launch-readiness call only reads the Pending policy and never dispatches work. + +## Evidence boundary + +Read the preserved baseline logs `/tmp/rheon-ui-scene-stability-disclosure-before.log` and `/tmp/rheon-ui-scene-stability-focus-before.log`: each selected exactly one test, failed once, and filtered 205 others. No reviewer test/build/native execution occurred. + +These egui tests use 820 logical pixels of height and do not establish the native 900×650 at 150% layout, actual viewport-handle pointer capture, screen-reader acceptance, or general focus behavior for every control/object/support. Parent's earlier native representative Radial/tree/picking/outline/X Move/Rotate/Sphere Size/Escape/camera isolation/close evidence is limited to those observed interactions on its exact earlier revision. The present repair still requires native verification; no broader runtime/scientific qualification is inferred. Production qualification remains Pending. + +## Final receipt + +Final reviewed source/test file SHA256: `46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390`. + +Read the before/after enumeration logs: each lists 206 tests and each of the three admitted names exactly once. Each accepted execution log selects one test, reports one pass and 205 filtered tests, with no ignored tests executed. The new disclosure and focus regressions pass in 0.04 s each; the existing authoring interaction regression passes in 0.10 s. Both baseline failures remain preserved and are distinguished above from the accepted repaired runs. + +Owner's locked desktop check completes successfully in 1.21 s and strict desktop/test Clippy completes successfully in 6.84 s. Formatting and diff-check logs are empty; owner reports exit 0. These compile/static checks do not execute numerical cases. Reviewer read the source and evidence only and performed no test/build execution. + +| Evidence `/tmp/rheon-ui-scene-stability-…` | SHA256 | +|---|---| +| `list-before.log` | `9918d22dadd297444ca45eb2b2fbab55ee7a0d8747b64129e1d6374b8db6c877` | +| `disclosure-before.log` | `d7782a15a521fbcfbe400e3538d0c1ab6c6d496f9fb8d1583937a28e5dc89d61` | +| `focus-before.log` | `59c843f1d1298f8f64c8036542d7d5e29d572899c8c6f23bee3cb4f6433af09d` | +| `list-after.log` | `37f111fa1e4e49623b13d3d31943161d2bd4fa15af48c381c47489ca9aeb27b1` | +| `disclosure-after.log` | `7920e6fcf08b502ba9a6d3ad8730101200a8384da47ed764fda2ab8a3f289f8e` | +| `focus-after.log` | `a025239c1e7e2565425f5603b5bd2620af8d9abc0e2193ffd2837b3a5293b2e2` | +| `authoring-regression.log` | `291acbf05100c55ed609432f4874ff88694e68c4eb62469e44653866895f67aa` | +| `check.log` | `5adfe47eb2a9b5b9ef51e4c77d25ade237255a8d7e514d584469a5c7619dee43` | +| `clippy.log` | `8c0e6e0907e6712eabdfcf565baacf868b526e4e3c55103788e4939398bc2251` | +| `fmt.log` | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | +| `diff.log` | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | + +No production path beyond the property UI identity scope changed. Native repair acceptance remains pending; authored-force qualification remains Pending. diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index bbc3a7a9..3903e124 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -380,9 +380,9 @@ impl SceneUi { } else if self.context==SceneContext::Draft { if self.edit_matches() { let mut candidate=self.edit.as_ref().unwrap().preview.clone(); - ui.add_enabled_ui(false,|ui|force_properties(ui,&mut candidate)); + draft_properties(ui, &mut candidate, false); } else if let Some(object)=self.forces.objects.iter_mut().find(|o|Some(o.id)==self.selected) { - let changed=ui.push_id(("force-properties",object.id.0), |ui| ui.add_enabled_ui(editable,|ui|force_properties(ui,object)).inner).inner; + let changed=draft_properties(ui, object, editable); if changed {self.validate();} if ui.add_enabled(editable,egui::Button::new("Remove force")).clicked(){self.remove_selected();} } else {ui.label("Select an object");} @@ -393,6 +393,17 @@ impl SceneUi { }); } } +// Normal editing and disabled drag previews are the same object's properties. +// Explicit Ui identity also keeps numeric auto-IDs stable when errors add rows +// before the scroll area. Retained properties use their separate existing scope. +fn draft_properties(ui: &mut egui::Ui, object: &mut ForceObject, enabled: bool) -> bool { + let id = ui.make_persistent_id(("force-properties", object.id.0)); + ui.scope_builder(egui::UiBuilder::new().id(id), |ui| { + ui.add_enabled_ui(enabled, |ui| force_properties(ui, object)) + .inner + }) + .inner +} fn quantity(ui: &mut egui::Ui, label: &str, value: &mut f64, unit: &str) -> bool { super::settings::quantity(ui, label, value, unit, 0.01, -f64::MAX..=f64::MAX).changed() } @@ -740,6 +751,141 @@ mod tests { assert_eq!(scene.forces, before); } } + // Context-only regressions: literal canonical metadata; no App, Worker, + // numerical constructors, filesystem, recorder, model or cleanup paths. + fn sphere_scene() -> SceneUi { + let mut scene = SceneUi { + open: true, + ..Default::default() + }; + scene.add(NewForce::Radial).unwrap(); + scene.forces.objects[0].support = ForceSupport::Sphere { + radius: 0.25, + edge_fraction: 0.25, + }; + scene + } + #[test] + fn scene_properties_disclosure_survives_preview_apply_and_cancel() { + for (header, field) in [ + ("Transform", "Position X"), + ("Support & activation", "Radius"), + ] { + let ctx = context(); + let mut scene = sphere_scene(); + let frozen = scene.freeze_for_run().unwrap().unwrap(); + let (output, _) = frame(&ctx, &mut scene, 1250.0, vec![]); + click(&ctx, &mut scene, 1250.0, &output, header, false); + let (output, _) = frame(&ctx, &mut scene, 1250.0, vec![]); + assert!(text_rect(&output, field, false).is_positive()); + for apply in [false, true] { + let base = scene.draft().clone(); + let mut preview = scene.selected_force().unwrap().clone(); + preview.transform.translation[0] += 0.1; + preview.support = ForceSupport::Sphere { + radius: 0.4, + edge_fraction: 0.25, + }; + assert!(scene.begin_edit()); + assert!(scene.preview_edit(Some(preview.clone()))); + for _ in 0..2 { + let (output, _) = frame(&ctx, &mut scene, 1250.0, vec![]); + assert!( + text_rect(&output, field, false).is_positive(), + "{header} stays expanded during preview" + ); + assert_eq!(scene.draft(), &base, "preview cannot mutate draft"); + } + assert_eq!(scene.finish_edit(apply), apply); + let (output, _) = frame(&ctx, &mut scene, 1250.0, vec![]); + assert!( + text_rect(&output, field, false).is_positive(), + "{header} stays expanded after transaction" + ); + if apply { + assert_eq!(scene.selected_force().unwrap(), &preview); + } else { + assert_eq!(scene.draft(), &base); + } + assert_eq!( + frozen.scene().objects[0].support, + ForceSupport::Sphere { + radius: 0.25, + edge_fraction: 0.25 + } + ); + } + } + } + #[test] + fn scene_invalid_radius_can_be_corrected_without_refocus() { + for width in [900.0, 1250.0] { + let ctx = context(); + let mut scene = sphere_scene(); + let frozen = scene.freeze_for_run().unwrap().unwrap(); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + click( + &ctx, + &mut scene, + width, + &output, + "Support & activation", + false, + ); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + let caption = output + .shapes + .iter() + .find_map(|shape| match &shape.shape { + egui::Shape::Text(text) + if text + .galley + .text() + .strip_suffix(" m") + .and_then(|v| v.parse::().ok()) + == Some(0.25) => + { + Some(text.galley.text().to_owned()) + } + _ => None, + }) + .expect("visible radius in metres"); + click(&ctx, &mut scene, width, &output, &caption, false); + let editor = ctx.memory(|m| m.focused()).expect("radius editor focused"); + replace(&ctx, &mut scene, width, "0"); + assert!(scene.error.is_some()); + assert!(scene.freeze_for_run().is_err()); + let (output, _) = frame(&ctx, &mut scene, width, vec![]); + assert!(output.shapes.iter().any(|shape|matches!(&shape.shape,egui::Shape::Text(text) if text.galley.text().contains("Invalid authored force")))); + assert_eq!( + ctx.memory(|m| m.focused()), + Some(editor), + "error banner must retain radius focus at {width}" + ); + replace(&ctx, &mut scene, width, "0.4"); + let _ = frame(&ctx, &mut scene, width, vec![]); + assert_eq!( + ctx.memory(|m| m.focused()), + Some(editor), + "correcting without a click retains focus" + ); + assert!(scene.error.is_none()); + assert_eq!( + scene.freeze_for_run().unwrap().unwrap().scene().objects[0].support, + ForceSupport::Sphere { + radius: 0.4, + edge_fraction: 0.25 + } + ); + assert_eq!( + frozen.scene().objects[0].support, + ForceSupport::Sphere { + radius: 0.25, + edge_fraction: 0.25 + } + ); + } + } #[test] fn scene_invalid_support_and_rotation_are_visible_without_snapshot_mutation() { let ctx = context(); From 6153a02a3583074794095bb9c50a753eec1a1214 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 02:10:44 +0000 Subject: [PATCH 22/25] Admit reviewed gated desktop force source closure Preserve the merged backend review and historical inventories, append the reviewed UI scope, and explicitly bind all 136 selected source paths. This admits fresh qualification while production force execution remains Pending. --- .../NATIVE_ADMISSION_INTEGRATION_REVIEW.md | 207 ++++++++++++++++++ ...n-native-sources-integration-20261014.json | 52 +++-- 2 files changed, 239 insertions(+), 20 deletions(-) diff --git a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md index 64884aa6..dcfbe041 100644 --- a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md +++ b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md @@ -1079,3 +1079,210 @@ No numerical kernel, tolerance, recorder/model contract, selector, qualification gate, historical inventory or A1 status changes. Initial 94062 execution receipts retain that historical subject; fresh clean committed qualification, affected checks and current-head hosted gates are required before integration. + + +## Shared authored force fields and accepted recording custody, 2026-10-20 + +This explicit source-admission extension covers the backend force-domain branch +at `43ec505218c43594641ecbb80873bec8c88d7d2c`, based on main +`fd7c0f0aee63bc8710df99f9bb01154e7619b974`. The admission commit changes only +this appended record and its version-three inventory binding; native source +bytes remain those of the reviewed subject. The complete conservative closure +grows from 124 to 126 paths: two added, eight changed, 116 unchanged, none +removed. Every `src/` path, build/toolchain input and existing independent +qualification input remains selected, including unchanged desktop sources. + +The bounded source scope is: + +- `src/force_field.rs` adds version-one authoring values, stable object IDs, + rigid transforms, enabled state, static half-open activation intervals, + directional/radial/vortex fields and global/sphere/box support with optional + smooth edge attenuation. Validated immutable snapshots share one evaluator. + Acceleration is in m/s²; force density is in N/m³ and requires positive + density in kg/m³. Finite-input, zero-distance and bounded-object checks + refuse unsupported or nonfinite evaluation. +- `src/forces.rs` shares the existing MAC face-update loop with authored + acceleration evaluation. Closed-box walls retain their existing treatment. + `src/simulation.rs` adds explicit force-aware fixed-box stepping and + success-stamped pressure/actual-force custody. The legacy pressure accessor + cannot expose incomplete force controls; candidate attempts invalidate the + scratch pressure stamp before use. Pressure projection itself is unchanged. +- `src/particle_fluid.rs` adds the same evaluator to gravity and pair forces + at pre-integration particle positions, using the evaluated local particle + density for force-density conversion. Accepted force controls are stamped + only on success; rejected attempts preserve the existing accepted particle + publication. Source emitters, constitutive stress and turbulence closures + remain distinct owners. +- `src/recording/force_fields.rs` adds a bounded version-one binary metadata + codec with exact float bits and scene validation. Force-aware run identity + binds the scene digest; append and read check run/publication agreement. + `src/recording.rs`, `recording/carrier.rs` and `recording/material.rs` + retain actual accepted force inputs and decode them for selected archives. + Historical unforced hash/control paths remain separate and unchanged. +- `Cargo.toml` adds an opt-in `force-serde` feature using existing dependency + identities; `Cargo.lock` is unchanged. `src/lib.rs` adds public exports. + No desktop/request/worker source is changed by this backend branch. + +Exact added/changed source SHA256 pins: + +- `Cargo.toml`: `f925dae7a0d74e6024e6e0d64962a275ae4f28fedec5b016341671021b8239ae` +- `src/force_field.rs`: `adecbc5f92b89179a2e38885be2acbcddd42df40061f00ffa5d758224ed045a5` +- `src/forces.rs`: `7b3b48c0813c995a937a44ca1b624411c59dde6419b62041b67456cde2d692d9` +- `src/lib.rs`: `30f5b78a98f6d0c010955a25fc00cc2de4aadf159d260e95afd3f3143dc6f9c3` +- `src/particle_fluid.rs`: `538c498122bbc1045ae59d7363ff87e1dd9a40003fb48eeae835228ad4c65893` +- `src/recording.rs`: `47b2b24bbe9af777ceafdefa6f0b82200a60cea590d0b64f326de1d50c6d7d2d` +- `src/recording/carrier.rs`: `5a9b10b929f650423ad9e58bd7047bfe80cc7bf18138d74bd628c2f3f17af711` +- `src/recording/force_fields.rs`: `2cf3ec9ec191c35c814d48ace301feaccd6cc73311a66970c9d112dad432c4a9` +- `src/recording/material.rs`: `e72e256aa3af46fef9002822de5281caf1de66ba070eb2109f77b083e20ae6ce` +- `src/simulation.rs`: `c08ede6330c4bf6ed7e858e556806ec80628522ceb42286cf52714bf7430c9a1` + +This admits an attempt at fresh clean committed-source native/rational +qualification only. It is not a numerical receipt, scientific validation of the +new force laws, native-window evidence or completed desktop parity. Pure +evaluator/codec checks and the seven bounded adapter/recording tests have their +own pinned subjects under `../plans/force-authoring/`; they do not substitute +for the fresh independent qualification required here. Later desktop/runtime +composition must re-establish complete admission and fresh evidence for its +actual committed source. + +The historical v1/v2 inventories and all preceding review text/receipts retain +their original bytes and subjects. The v3 ancestry/schema and predecessor +digests remain authoritative. No source selector, numerical threshold, +qualification runner, mathematical oracle, workflow or immutable primary +identity is changed. A stale or malformed present inventory must still refuse +admission rather than fall back. Fresh receipt binding must include the actual +committed manifest and this complete review record; earlier standalone CI +success or historical receipts cannot qualify this admission commit. + +Independent review of this exact two-file candidate is required before atomic +commit and publication. This record contains no claim that an unseen patch was +previously approved. Native/rational CI uses its existing 20-minute job bound. +The separate research-book job performs admission preflight and real packet +rejection tests normally and under optimized Python within its existing +90-minute job bound; readiness requires those actual current-head outcomes, +not an assumption that the standalone qualifier includes packet tests. + + +## Scene authoring and gated force runtime integration, 2026-10-21 + +This explicit extension admits the complete UI source at +`da5c68891a7c627007ccb7136c9c9cd6b42fee26`, tree +`281834746edcb4c84e1d33a9b0eddb9578fda3b0`, for an attempt at fresh +committed-source qualification. Its complete selected closure is compared +against merged backend `7516a8e5d328c6dc8eda8ea577cb03f20b2e343a`, whose +126-path inventory includes the preceding shared-force admission. The UI +closure has 136 paths: ten added, ten changed, 116 unchanged, none removed. +The existing selector still includes every `src/` file, including desktop +tests and README files, plus build/toolchain and qualification inputs. + +The complete merged backend review above is preserved byte for byte. The UI +branch's older 124-path inventory and shorter review do not supersede the +merged 126-path backend admission. This v3 rebind preserves all 116 unchanged +backend pins and explicitly adds or replaces only the twenty UI paths listed +below. Historical v1/v2 records, predecessor digests, reviewed ancestry and +immutable primary identity remain unchanged. + +The reviewed UI scope is: + +- `scene_ui.rs` owns canonical authored force objects, shared-domain IDs, + validated properties and immutable snapshot freezing. Draft and Retained + selection/property contexts remain distinct; retained values are read-only. + `scene_view.rs` and `scene_handles.rs` add viewport selection, thin selected + outlines and Move/Rotate/Size previews. Handle preview data is temporary; + validated release applies through the existing transaction, while cancel + leaves canonical draft data unchanged. These UI controls do not define a + second force evaluator or alter physical units. +- `rheon-desktop.rs`, `carrier_execution.rs`, `comparison.rs`, `run_sets.rs` + and `run_sets/source.rs` carry immutable track-specific snapshots through + native-carrier and particle-material dispatch and retained presentation. + Force-aware execution uses the canonical backend adapters and accepted + controls. Saved native intents use version two for authored metadata while + preserving the legacy no-force version-one bytes/hash path. This does not + add a serialized particle-material cold-rerun API. +- `inspector.rs` binds and verifies the force-scene digest using canonical + recording metadata before publication/readback. Historical no-force + branches retain their existing controls. `settings.rs` supplies precise + numeric editing; `configured_controls.rs` and `viewport.rs` integrate the + compact timeline, Scene panel and selection/focus ownership. +- `force_run_policy.rs` distinguishes eligible native-carrier and + particle-material targets from unsupported configured/session/provider/ + free-surface/AI paths. Production qualification remains `Pending` and + authored production launches remain refused. Test qualification cannot + grant capability to an unsupported execution owner. The associated policy, + runtime, setup and timeline test modules are selected source inputs, not + evidence that all runtime or native acceptance is complete. +- `native_presentation_harness.rs` is a test-configuration presentation + harness. Its presence in the conservative closure does not establish native + execution results. Backend force equations/evaluator, MAC/particle adapters, + accepted recording modules and numerical/pressure policies are unchanged + from merged `7516a8e5`. + +The final two repairs were independently inspected as actual source diffs. +`37ac711a4b7134f628c7a71d54f62056d71ca5f3` moves unchanged Inspector custody +helpers and the test-configuration entry point before their test modules, +without lint suppression. `da5c6889` gives normal Draft property editing and +disabled previews one persistent per-object UI identity, preserving disclosure +state and numeric editor identity when invalid-input feedback adds rows. Its +two new regressions construct canonical metadata and egui contexts only; they +do not construct App/Worker or execute solvers, recorders or models. + +The published source reviews and bounded execution subjects remain under +`../plans/ui-refinement/reports/`, particularly +`runtime-force-integration-source-review.md`, +`force-worker-runtime-results-review.md`, `typed-force-policy-source-review.md`, +`ci-item-order-source-review.md` and `scene-properties-stability-source-review.md`. +Their stated subjects and limits remain authoritative. Independent read-only +integration review checked the twenty added/changed UI paths, both exact repair +diffs, all transferred changed-file identities, full reconstructed trees and +the complete selected-source comparison against merged `7516a8e5`. This is a +source review; it adds no numerical, hardware or native-interaction result. + +Exact added/changed source SHA256 pins relative to merged `7516a8e5`: + +- `src/bin/desktop/carrier_execution.rs`: `c4455879373fc71eaaf398fdaa230c4d0b115dc7ff0a9da6356afeb24cba23b2` +- `src/bin/desktop/comparison.rs`: `27ad4b88db848430a68f5f9fae796fa10eab1df26af9dbae73386e55c9b5c74f` +- `src/bin/desktop/configured_controls.rs`: `5463e18b3b5a139c448dd327b23318605664e90aea4690187981177e7f68e60b` +- `src/bin/desktop/configured_worker_tests.rs`: `dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016` +- `src/bin/desktop/force_run_policy.rs`: `086d4ee8c18ff30f2165b8622a53d649bd9eb8166bbbbcb82dde3a0eb2427cc2` +- `src/bin/desktop/force_run_policy_tests.rs`: `a518b47b71cb2aff5d7ce46169918c60d534c00ffccdd0ee5c90abcebbdcdebd` +- `src/bin/desktop/force_runtime_integration_tests.rs`: `da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584` +- `src/bin/desktop/force_runtime_ui_tests.rs`: `1d242eaed1b416e3dfb2577d5610c4e0d6d0ca5040f80293681ae184090cebb9` +- `src/bin/desktop/inspector.rs`: `7a6582faf9bfff755d7fade624f6b11cca06813c75310fba25ccbfabff1d2aae` +- `src/bin/desktop/native_presentation_harness.rs`: `38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6` +- `src/bin/desktop/run_sets.rs`: `b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0` +- `src/bin/desktop/run_sets/source.rs`: `39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf` +- `src/bin/desktop/scene_handles.rs`: `4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22` +- `src/bin/desktop/scene_ui.rs`: `46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390` +- `src/bin/desktop/scene_view.rs`: `d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f` +- `src/bin/desktop/settings.rs`: `72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4` +- `src/bin/desktop/setup_ui_tests.rs`: `db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900` +- `src/bin/desktop/timeline_ui_tests.rs`: `606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b` +- `src/bin/desktop/viewport.rs`: `ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3` +- `src/bin/rheon-desktop.rs`: `dfc472b78c46e83b45668a8d99bb82f721fcc771ddc584cdc8fdaf82272003ff` + +This two-file admission commit changes no selected native source byte. It +admits fresh clean committed-source native/rational qualification only; it is +not a scientific receipt for authored fields, native repair acceptance, +production enablement or full voxel/particle desktop parity. Earlier bounded +worker/recording runs and local egui checks retain their own exact subjects. +The assigned native validator must separately verify the final repair's +specified window/scaling and interaction cases. A later enabled candidate +requires its own reviewed source, admission binding and separately bounded +actual App validation. Unsupported execution paths remain unsupported. + +No selector, threshold, mathematical oracle, qualification runner, workflow or +historical receipt is changed. A stale present inventory must still refuse; +there is no fallback to older pins. Fresh receipts must bind this actual +committed complete inventory and review, rather than reuse backend or earlier +UI receipts. The existing dedicated native/rational workflow has a 20-minute +job bound; the separate research-book job performs admission preflight and +real packet rejection tests normally and under optimized Python within its +existing 90-minute bound. Both actual exact-head outcomes remain required. +The dedicated workflow is not automatically selected by admission-document +paths and must be dispatched explicitly for the clean committed branch. + +Independent review of the actual complete two-file candidate is required +before atomic commit and publication. No prior review is represented as +approval of an unseen patch. PR147's separate research-book/FEM changes are +outside this source admission; current main and dependencies must be checked +again before any merge. diff --git a/docs/education/aligned-strain-native-sources-integration-20261014.json b/docs/education/aligned-strain-native-sources-integration-20261014.json index 2169dc08..a0499313 100644 --- a/docs/education/aligned-strain-native-sources-integration-20261014.json +++ b/docs/education/aligned-strain-native-sources-integration-20261014.json @@ -1,16 +1,16 @@ { "schema": "rheon-reviewed-aligned-strain-native-sources-v3", "reviewed_base": "21d4122c3d0edb2ca5c3ce089175c70035d737a3", - "scope": "Independently reviewed 1000 cells/m and 10000-axis native/independent-preview controls, including honest diagnostic preflight work/reference-memory accounting; complete 124-path closure with 15 approved changes and 109 unchanged inputs. Fresh committed-source native/rational and hosted acceptance required; measured A1 remains blocked.", + "scope": "Explicit reviewed Scene authoring and gated native-carrier/particle-material runtime integration at da5c688, preserving the merged backend 7516a8e force admission; complete 136-path closure with 10 added, 10 changed, 116 unchanged and none removed relative to the merged 126-path admission. Source admission only; production qualification remains Pending, unsupported paths remain refused, and fresh committed-source native/rational and packet/publication checks remain required.", "preserved_inventory_sha256": { "docs/education/aligned-strain-native-sources.json": "84c036618b32de66623d8ded24ae8cf95194457e36b9a130087a9c2bf3bd3f6a", "docs/education/aligned-strain-native-sources-runtime-20261013.json": "6ada2133501aab449ed7ee73f802d8a4593db50c857f0a8f8ce2af4507721d6d" }, "review_record": "docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md", - "review_sha256": "5836c3fa71bf20b72dbde79a2f5d27dd281ffc3930e0af7ad81f9039fd3edd42", + "review_sha256": "3e190beb40264f575a54e36ee33160ae3da73e36fa7032c67702ab8a8f2ff48a", "source_sha256": { "Cargo.lock": "3b5836a7be9f1290c698cadaff010e11b93e33b4af839c8549391c23e7d8ec0c", - "Cargo.toml": "10a7b438ec5e7bdb2c8eb95137cf5a42d1cd35687ed47bf63b05a3e42dc1674e", + "Cargo.toml": "f925dae7a0d74e6024e6e0d64962a275ae4f28fedec5b016341671021b8239ae", "examples/aligned_strain.rs": "11910f4536bbb0057bd7462864f4d2af07d37dcd02103f41d1b93674dcca4547", "rust-toolchain.toml": "09ff66fa19e0cb8bbad00b6032e28f1cf77b0a37a73013648177b35297f98800", "src/advection.rs": "0163a872c314b2b3bb7f732348aafdbfa7b162dceea74807b7e7e3eb60ab0849", @@ -41,32 +41,42 @@ "src/bin/desktop/archive_view.rs": "9f96f1d6b11b46e7fd523825e19a8fc0e0ec862669acd129f5e39e91f83c89ff", "src/bin/desktop/build_identity.rs": "a94fea1f039a49f8d0eb79b019b3fbd0f2c9da1762ba854d10cba1e5fd87a25b", "src/bin/desktop/camera.rs": "b6bcc84a45bd5979de357c965c84426c25e81161ee1f06022e57a93a46643f7d", - "src/bin/desktop/carrier_execution.rs": "0d96fb2acdfd63374e5f0004d9ead8b43d9fe2f6959c0d01f9c828458ae1799e", + "src/bin/desktop/carrier_execution.rs": "c4455879373fc71eaaf398fdaa230c4d0b115dc7ff0a9da6356afeb24cba23b2", "src/bin/desktop/collision_ui.rs": "78d7be8e49798e2420ebac5c3fb756e1b195e43ef113ae03ce1b2d3362009204", - "src/bin/desktop/comparison.rs": "ef52e29f4d9c59007c11ff973ed6a43c33ac9e1d583819ede11ebd7aea9ec0a5", + "src/bin/desktop/comparison.rs": "27ad4b88db848430a68f5f9fae796fa10eab1df26af9dbae73386e55c9b5c74f", "src/bin/desktop/comparison_archive.rs": "813cc86187ef7313deacbe9d9cd15340f37ecdf62f5234a45f033909415459b9", "src/bin/desktop/comparison_field_view.rs": "ec8ea0799522d5a7fadac47b06b42f26717f935bbb9a1677fc5052f096c1cc82", "src/bin/desktop/configured_comparison_tests.rs": "e6716e8c707f30cc20304b16e3201f8fb1fd838650bd08a43a95ab4d68cbbf46", - "src/bin/desktop/configured_controls.rs": "05d317554c4db033dd35c5e153dceaea34367eed33125ec8c7f59466f83ede36", + "src/bin/desktop/configured_controls.rs": "5463e18b3b5a139c448dd327b23318605664e90aea4690187981177e7f68e60b", "src/bin/desktop/configured_producer_exit_tests.rs": "7560ff5395acbd50ce111b1e50ae5e8aca1b1bab5356ec195929eec0994f24bb", - "src/bin/desktop/configured_worker_tests.rs": "81e24d69cd5838de3becbc31453ce0a10947e7fe956287862957fcc06eae6e38", + "src/bin/desktop/configured_worker_tests.rs": "dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016", "src/bin/desktop/emitter_controls.rs": "d5eaf6358b037d1f11177abbbd83bfa8aa18c263b195c3911b8f183b3334fba9", "src/bin/desktop/engineering.rs": "9fc4eaafd1118abda50b70d1cc71c843fc1ca71d420d2cd47f16a20fcc9e8e9d", "src/bin/desktop/export_ui.rs": "1702f437b17dd42d2bf1311ca14d0301a53712392abc6591ab8962b48cba511d", + "src/bin/desktop/force_run_policy.rs": "086d4ee8c18ff30f2165b8622a53d649bd9eb8166bbbbcb82dde3a0eb2427cc2", + "src/bin/desktop/force_run_policy_tests.rs": "a518b47b71cb2aff5d7ce46169918c60d534c00ffccdd0ee5c90abcebbdcdebd", + "src/bin/desktop/force_runtime_integration_tests.rs": "da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584", + "src/bin/desktop/force_runtime_ui_tests.rs": "1d242eaed1b416e3dfb2577d5610c4e0d6d0ca5040f80293681ae184090cebb9", "src/bin/desktop/history.rs": "e4be154bbbf40a36f370b68ec9e15fea663fb589d476a43eb298c0e8c0f0d821", "src/bin/desktop/identity.rs": "df451a986476b9df252a9a4dbc1e33218bf5ab3cb268b83fa78b47dfffdae184", - "src/bin/desktop/inspector.rs": "bb6bb83529896323fbf5414d6e7f41f8462b21e80a6d1f620edb6eecd2fc6649", + "src/bin/desktop/inspector.rs": "7a6582faf9bfff755d7fade624f6b11cca06813c75310fba25ccbfabff1d2aae", + "src/bin/desktop/native_presentation_harness.rs": "38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6", "src/bin/desktop/particle_view.rs": "75208acd210d4558a34127b98cf77f2c898a560f991f6bed3bee3a4eb160b9a2", "src/bin/desktop/run_set_view.rs": "ab2c841612f39b16f5bd1a363593eae5388bf4a81ec495dfb89e43bd7436580b", - "src/bin/desktop/run_sets.rs": "e194a48350954edf13cdcd01371d320cc210069b4256ba955f2ddd1132301cc1", + "src/bin/desktop/run_sets.rs": "b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0", "src/bin/desktop/run_sets/configured.rs": "45fa8cf6976da7f8ab3eff27fd69ba5511c98c4be541739eefcdf762826abac9", "src/bin/desktop/run_sets/configured/archive.rs": "eb1d8e77019eb57afd9e5fd5f57af55f8ff320527ec8efc935945bd7ff477806", - "src/bin/desktop/run_sets/source.rs": "c948c24fbe3d0bbb4d281c427244f7908a16506236c0f24b194490c64033cd11", - "src/bin/desktop/settings.rs": "d66735427fc8b6be57d93812d30fb4b8c357419acd70b7b0564aa48216bb2660", + "src/bin/desktop/run_sets/source.rs": "39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf", + "src/bin/desktop/scene_handles.rs": "4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22", + "src/bin/desktop/scene_ui.rs": "46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390", + "src/bin/desktop/scene_view.rs": "d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f", + "src/bin/desktop/settings.rs": "72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4", + "src/bin/desktop/setup_ui_tests.rs": "db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900", + "src/bin/desktop/timeline_ui_tests.rs": "606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b", "src/bin/desktop/transport_view.rs": "e8c3d5cc588d4efac9918544fc5012696a05bdfa7adcba7ce18b07c5a260f6b8", - "src/bin/desktop/viewport.rs": "599046403df150499064337c2ce3b6996f1ece69f4e876f882200f079b7cd530", + "src/bin/desktop/viewport.rs": "ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3", "src/bin/rheon-ai-compare.rs": "00000ec93161c04d9ff0515e5ae2ab6855a5467f56f23a4d6fc6c1059ef8ada9", - "src/bin/rheon-desktop.rs": "76a92d5c928919f832904395319644f14eb36bc2e1c33aeab1146d4b1480ce48", + "src/bin/rheon-desktop.rs": "dfc472b78c46e83b45668a8d99bb82f721fcc771ddc584cdc8fdaf82272003ff", "src/bin/rheon.rs": "5116a121256c8bb57d086caedbda8151f3593cb99d3e84b1d04fea25b12dfb51", "src/box_flux.rs": "554b3cdc916e7527bbfdbd6a54b8cf7e27549c3c661015b259c8b2d18784bbb0", "src/box_flux_step.rs": "e2ef3afcd8adffe0f79c0030eb56e5bb55943775e474a1dab2a7fe78ac38441b", @@ -83,10 +93,11 @@ "src/export.rs": "afe2b79d11f1b8c0ccd16dcaae51a22c8d35ba2e7a23c362729fe907b208f0d3", "src/fitted_height.rs": "0181db7ffbad932b7e3492ce1a8e260736389e4ded3472c81fe2697861f6777a", "src/fixed_bottom_ale.rs": "3cc724d376d115fccd8b6c658d7f26b6436f4d13d105a5ae8416fd9c04b17a47", - "src/forces.rs": "c5585940330096890016ff68f25645aa12cdaec2b796a8b234c138e88dd892bb", + "src/force_field.rs": "adecbc5f92b89179a2e38885be2acbcddd42df40061f00ffa5d758224ed045a5", + "src/forces.rs": "7b3b48c0813c995a937a44ca1b624411c59dde6419b62041b67456cde2d692d9", "src/free_surface.rs": "f43fd9a71da6aac985dfcfb6cc471318227c1727d6e0dad8e2a5feb5b3728a8e", "src/geometry.rs": "712d04f27d55d7e77f17effefbf2660ca3b8a000de5b7aa51b42b89fe5d3b634", - "src/lib.rs": "c6dfcf187c1cb2bb64835bd7c72a7094b6127730c55bb6ee47d2e676354317ff", + "src/lib.rs": "30f5b78a98f6d0c010955a25fc00cc2de4aadf159d260e95afd3f3143dc6f9c3", "src/liquid_step.rs": "f99dce6a99f6af8a9aef2436af7f33de58dac3be5998a48e9187fbdf2acc3f93", "src/liquid_volume.rs": "24623038f62261ce6a17b034e9b6d82c5f0aba02dd0df7aad174c885ed2a8c96", "src/material.rs": "49874ff972f16a972d706236b34e62f83e994c57e39b642c6d94a1a6f4b7fb66", @@ -99,15 +110,16 @@ "src/obstacle_shear.rs": "745da07d5e949276a2e0cb7ac4bdde4aedf557bf6189d5a51581d8ce79b1dc1f", "src/operator.rs": "cd98684d5204908e5344b2628a8f93ea2a55a90c6678e03a71f2d75ab1f2be38", "src/parallel.rs": "ee5420673a59ae4fb247b3542878e74902f7a87056744d4303fb400be074677e", - "src/particle_fluid.rs": "ed4ab916e602a55b2245beba5bdbff268fcb704b67fa41248d47ad2598f2468f", + "src/particle_fluid.rs": "538c498122bbc1045ae59d7363ff87e1dd9a40003fb48eeae835228ad4c65893", "src/pressure.rs": "39ddb79dd3de196e83545679e556c0cd8ce9afae9db9282161f54b8f22f713a5", "src/pressure_session.rs": "ae687a8822dd6cfeadf5dc6e13e7983d8d1afcdf9e5db1857d4dce64cd0e3fb8", "src/provider_step.rs": "79167b70a8d164a2367f43816feab443eefb41f23bc4bd940716dd5fd32d495b", - "src/recording.rs": "d022f8f46c288538298896ae444c9780265a6bf77fa9a4e1ba4b9732cf026eb2", - "src/recording/carrier.rs": "b9c83015df214b426315f1840cbd2029d3f4b85525aef2fc8592e97468d54509", + "src/recording.rs": "47b2b24bbe9af777ceafdefa6f0b82200a60cea590d0b64f326de1d50c6d7d2d", + "src/recording/carrier.rs": "5a9b10b929f650423ad9e58bd7047bfe80cc7bf18138d74bd628c2f3f17af711", "src/recording/comparison.rs": "5a82201f4583cf9eb4cc38d43c7ee88bc54d10a84df46272c2dd84091aa4a307", "src/recording/diagnostic.rs": "279aff886f1354af203429a2e43cce73cdb485ee5ddd90201d1498e5e7872b7f", - "src/recording/material.rs": "e01a730b04fd34d7fdc797ede735cb45fa743101dd51e8fd406b02bacdd43d00", + "src/recording/force_fields.rs": "2cf3ec9ec191c35c814d48ace301feaccd6cc73311a66970c9d112dad432c4a9", + "src/recording/material.rs": "e72e256aa3af46fef9002822de5281caf1de66ba070eb2109f77b083e20ae6ce", "src/recording/provider.rs": "980ea5af7094016b13e7d0f39110aa2ff673b0173416e0fe583e6266b7c27f57", "src/recording/provider/comparison.rs": "90c1230c2cbfb2415c74e5d3c9b66e74f68c7b2096bf4063c5513ac2c6feaa75", "src/recording/provider/runtime.rs": "d7b2a1f7cfa866b33887f7c8f804999058d6e70a1c9d71421372d151152ebccb", @@ -116,7 +128,7 @@ "src/session.rs": "367c370e2257e35de09943e8dc3e1257b994c5e01d1409af1a989682f41c583e", "src/session/configured.rs": "d99ae990e9c7ddc956f95f414a25832411a4dc84ab434df95ff9f8f68cf52a83", "src/sgs.rs": "0b4140af63144df8ff1839782b3c70295c18b96decc67aba348a37a33e9f996d", - "src/simulation.rs": "8dea790daf2a2d0801db3003094f9fffe034c9b87946d520a1eacc266c95f078", + "src/simulation.rs": "c08ede6330c4bf6ed7e858e556806ec80628522ceb42286cf52714bf7430c9a1", "src/solver_provider.rs": "0ab7db08a9016e73d664c730e4ff513d42599a84c680ea061e6604859432a5a5", "src/solver_provider/configured.rs": "124ca65dd1dec3a8a89043198fb085453a42ca8aff0dc87c67b04b6a986109be", "src/solver_provider/context.rs": "763debfefc6f9375849c8f503995e20660fc25efef9e827d5942d980e14fe693", From 3ce3f29cdec0613272064eb8df2f4fbad6ee0603 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 02:45:46 +0000 Subject: [PATCH 23/25] Clarify native and configured intent rejection contracts --- docs/api/saved-run-sets.md | 18 +++++++--- tests/configured_run_sets_contract.rs | 5 ++- tests/saved_run_set_contract.rs | 50 ++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/docs/api/saved-run-sets.md b/docs/api/saved-run-sets.md index 85a9761f..8eac05cb 100644 --- a/docs/api/saved-run-sets.md +++ b/docs/api/saved-run-sets.md @@ -39,10 +39,20 @@ existing recording owner remains authoritative for all fields and lifecycle data ## Persistence and reopening -JSON uses explicit schema version 1, fixed typed fields, bounded inputs and exact -round-trip f64 values through the existing pinned serde_json float-roundtrip -feature. Spec and run-set digests hash their typed serialized payloads; these are -content identities, not signatures. Unknown versions return Unsupported; unknown +Run-set JSON remains schema version 1. Native frozen specs retain version 1 for +historical no-force intent and use version 2 when an authored force document is +bound; version-1 bytes and digests remain unchanged. Both use fixed typed fields, +bounded inputs and exact round-trip f64 values through the existing pinned +serde_json float-roundtrip feature. Spec and run-set digests hash their typed +serialized payloads; these are content identities, not signatures. + +Configured accepted-Session specs own a separate, kind-tagged version-2 shape. +The shared number 2 does not make the two document families interchangeable: +opening a configured spec with the native reader fails strict typed decoding +with `Codec`. The former `Unsupported` classification relied on native version 2 +being unknown; native force intent now intentionally supports that version. +An otherwise valid native spec carrying an unsupported future version still +returns `Unsupported("frozen native intent version")`. Unknown keys, variants, duplicates or malformed JSON fail decoding. Changed content with an old digest returns Integrity. Files are limited to 1 MiB and sets to 64 members. No migration, reinterpretation of saved Debug text, automatic recovery overwrite, diff --git a/tests/configured_run_sets_contract.rs b/tests/configured_run_sets_contract.rs index 624cd809..68fd365f 100644 --- a/tests/configured_run_sets_contract.rs +++ b/tests/configured_run_sets_contract.rs @@ -339,9 +339,12 @@ fn immutable_two_variant_sets_roundtrip_and_native_schema_one_stays_separate() { .spec .save_new(&dir.0.join("spec-v2.json")) .unwrap(); + // Native force intents also use v2, so the numeric version alone no longer + // rejects this configured-domain document. Its kind/constructor/future + // shape must still fail the native reader's strict typed decoding. assert!(matches!( FrozenSpec::open(&dir.0.join("spec-v2.json")), - Err(RunSetError::Unsupported(_)) + Err(RunSetError::Codec(_)) )); assert!(matches!( RunSetRevision::open(&dir.0.join("set-v2.json")), diff --git a/tests/saved_run_set_contract.rs b/tests/saved_run_set_contract.rs index b8128ad1..26f28ff3 100644 --- a/tests/saved_run_set_contract.rs +++ b/tests/saved_run_set_contract.rs @@ -450,6 +450,54 @@ fn corrupt_changed_and_unsupported_members_never_substitute_live_data() { )); std::fs::remove_dir_all(root).unwrap(); } +#[test] +fn native_spec_v1_bytes_force_v2_and_future_version_refusal_are_distinct() { + // Literal legacy bytes/digest, independent of the native intent codec. + // Only metadata validation, hashing and JSON: no owner, stepping or I/O. + const V1_SPEC: &str = r#"{"counts_xyz":[2,2,2],"spacing_m":[0.5,0.5,0.5],"origin_m":[0.0,0.0,0.0],"density_kg_m3":2.0,"pressure_method":"jacobi_pcg_v1","tolerances":{"relative_residual":1.0,"absolute_residual_m3_s2":1.0,"predicted_divergence_per_s":1.0,"actual_divergence_per_s":1.0,"max_courant":1.0,"max_pressure_iterations":4},"requested_dt_s":0.125,"requested_steps":1,"source":null,"simulation_memory_limit_bytes":1048576,"recording":{"max_database_bytes":65536,"max_record_bytes":16384,"max_records":2}}"#; + let legacy = FrozenSpec::new(serde_json::from_str(V1_SPEC).unwrap()).unwrap(); + assert_eq!(serde_json::to_string(&legacy.spec).unwrap(), V1_SPEC); + assert_eq!( + legacy + .sha256 + .iter() + .map(|v| format!("{v:02x}")) + .collect::(), + "5969e6f1a25728943a3a1119d4f393f33a4bb45af71a5d99a54551d06d569519" + ); + let legacy_bytes = format!( + "{{\"schema_version\":1,\"sha256\":{},\"spec\":{V1_SPEC}}}", + serde_json::to_string(&legacy.sha256).unwrap() + ); + assert_eq!( + serde_json::to_vec(&legacy).unwrap(), + legacy_bytes.as_bytes() + ); + assert_eq!( + FrozenSpec::from_bytes(legacy_bytes.as_bytes()).unwrap(), + legacy + ); + + // Some(empty) is still an authored native v2 intent, not a configured v2 + // document or the historical no-force v1 branch. + let forced = legacy + .clone() + .with_force_fields(&rheon::ForceScene::default().freeze().unwrap()) + .unwrap(); + assert_eq!(forced.schema_version, 2); + assert_ne!(forced.sha256, legacy.sha256); + let forced_bytes = serde_json::to_vec(&forced).unwrap(); + assert_eq!(FrozenSpec::from_bytes(&forced_bytes).unwrap(), forced); + for supported in [legacy, forced] { + let mut future = serde_json::to_value(supported).unwrap(); + future["schema_version"] = serde_json::json!(99); + assert!(matches!( + FrozenSpec::from_bytes(&serde_json::to_vec(&future).unwrap()), + Err(RunSetError::Unsupported("frozen native intent version")) + )); + } +} + #[test] fn bounded_strict_manifests_reject_versions_keys_duplicate_keys_and_mutation() { let root = directory(); @@ -464,7 +512,7 @@ fn bounded_strict_manifests_reject_versions_keys_duplicate_keys_and_mutation() { .unwrap(); assert!(matches!( FrozenSpec::open(&path), - Err(RunSetError::Unsupported("manifest version")) + Err(RunSetError::Unsupported("frozen native intent version")) )); std::fs::write( &path, From 21e70bdeb42f529b63ee3cab5a08331c09646c9f Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 04:23:24 +0000 Subject: [PATCH 24/25] Enable reviewed production force dispatch with shared eligibility policy --- .../NATIVE_ADMISSION_INTEGRATION_REVIEW.md | 91 +++++++++ ...n-native-sources-integration-20261014.json | 26 +-- .../enabled-force-app-validation-contract.md | 175 ++++++++++++++++++ src/bin/desktop/configured_worker_tests.rs | 2 - src/bin/desktop/force_run_policy.rs | 12 +- src/bin/desktop/force_run_policy_tests.rs | 50 ++++- .../force_runtime_integration_tests.rs | 1 - src/bin/desktop/force_runtime_ui_tests.rs | 18 +- .../desktop/native_presentation_harness.rs | 10 +- src/bin/desktop/scene_ui.rs | 118 ++++++++---- src/bin/desktop/setup_ui_tests.rs | 6 - src/bin/desktop/timeline_ui_tests.rs | 2 - src/bin/desktop/viewport.rs | 5 +- src/bin/rheon-desktop.rs | 28 +-- 14 files changed, 436 insertions(+), 108 deletions(-) create mode 100644 docs/plans/ui-refinement/reports/enabled-force-app-validation-contract.md diff --git a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md index dcfbe041..9dabdb9c 100644 --- a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md +++ b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md @@ -1286,3 +1286,94 @@ before atomic commit and publication. No prior review is represented as approval of an unseen patch. PR147's separate research-book/FEM changes are outside this source admission; current main and dependencies must be checked again before any merge. + + +## Reviewed production-force enabling source candidate after qualified 3ce3f29 + +The preserved review prefix above is byte-identical to the complete reviewed UI +admission at3ce3f29cdec0613272064eb8df2f4fbad6ee0603: prefix SHA256 +`3e190beb40264f575a54e36ee33160ae3da73e36fa7032c67702ab8a8f2ff48a`. +Its v3 manifest SHA256 is +`15e3f33639805229a22822137331e337f7e32b1461373563ecaa4d9ddc716300`. +That checkpoint's Rust39534833351, native/rational39534867735 and book39534833251 +completed successfully; actual native/rational payload/source bindings and book +packet26normal+26optimized/no-packet-skips logs were independently reviewed. Its +PR deployment was skipped. Those receipts stay bound to3ce and are not receipts +for this new candidate. Historical6153/fe9 failures and cancellation remain evidence. + +Parent assigned these desktop paths and admission files to the repository +integration owner while the original UI owner is idle. The actual eleven-file +source patch over3ce was independently reviewed as SHA256 +`399db2ece70725d0e66eba4dce7abfeaf9f0d04bdfd710e2ba3009aa5c814f27`. +It removes every Request force_validation field, literal and cfg(test) branch. +Production and tests now use the same Validated policy for native-carrier and +particle-material execution. Configured/session/provider/free-surface/AI paths +still refuse authored presence, including empty/disabled/zero scenes. Unsupported +configured precedence, material-only single-track restriction, exact member force +binding and legacy None behavior remain. No solver equation, core adapter, +recording schema/hash, source emitter, material stress, closure or threshold changes. + +Scene receives the effective target and derives its warning from the same policy; +the Run action emits intent only for eligible valid input. A live force edit must +be applied/cancelled before launch, avoiding a snapshot of its pre-edit draft. +The viewport's reachable stale runtime wording is corrected while alternate +lab/session restrictions remain. Other modified test/harness files only remove +obsolete Request fields. Existing historical native property acceptance is on da5; +this new source candidate still requires its own production App validation. + +Strict desktop all-target Clippy and compile-only checking passed. The first new +Scene test initialization triggered field_reassign_with_default; that original +failure log was retained and the initializer corrected without suppressing lint. +A sealed current test binary SHA256 +`6f07fa1e27b8dcd3f007b5722eba414ba6e1228e36d2d6dd8c30aa7645043ca5` +enumerated208tests. Ten independently reviewed exact pure tests each passed once, +serially, under30s wall/20sCPU/512MiBAS/1MiBfile+log, TERM then owned-child KILL +with1s grace, testthreads1 and no parallel feature pool. They cover four force +policy cases; mixed saved-track custody and recording-header preflight; supported/ +refused Run intent without dispatch; Scene-only warning; disclosure stability; +and invalid-radius correction without refocus. No Worker, solver, SQLite, model, +native App, L1/MMS or extra scientific execution was performed locally. + +The actual-App input/allocation and admission protocol is +`docs/plans/ui-refinement/reports/enabled-force-app-validation-contract.md`, SHA256 +`80043585fd578cd625d5b2a24b4b66173fd852eed9643a50d82338a0908f0d10`. +It explicitly addresses real GUI carrier density1/192MiB/max2000, material's +minimum32768 allocation cap/default192MiB, recording1MiB/2records, exact small +finite workloads and separate rendering/process/temporary-disk overhead. It +requires native-owner existing GUI-compatible enforcement facts and an independent +actual execution-contract review before any App numerical execution. Managed +buffer arithmetic and prior Worker caps are not represented as process/RSS/disk +peak bounds. The native protocol's unresolved fields are not waived by this source +admission. No material serialized cold-rerun format or scientific parity is claimed. + +The complete conservative native selection remains136paths:0added,11changed, +125unchanged,0removed relative to qualified3ce. Every unchanged selected path was +verified against its previous pin; only the eleven independently reviewed source +hashes below are rebound. The v1/v2 inventory files, bindings, historical reviews, +qualification tools, admission selectors, packet rejection tests and thresholds +remain unchanged. The new protocol is a documentation input, not a new selected +native source path. + +| Reviewed changed path | Candidate SHA256 | +| --- | --- | +| `src/bin/desktop/configured_worker_tests.rs` | `06a9e7f5724b73c101a38dcc2c966d37b640900495643ea7bc04c13f4630f278` | +| `src/bin/desktop/force_run_policy.rs` | `cbb784c1cc403cfd524b715a52144c662f428b302014af1794bb3131d09bb97a` | +| `src/bin/desktop/force_run_policy_tests.rs` | `99d6b9e8b2a98eceab0514510d3f54873da50fdabc7626dd3896d45dd3c53383` | +| `src/bin/desktop/force_runtime_integration_tests.rs` | `04ba4f5883e48c5548cb420e17b6a6736436871d8e6c7792487939bfe5ed29a6` | +| `src/bin/desktop/force_runtime_ui_tests.rs` | `92475c2b577510a8f8fc11217f7eb1a5361dcaef20ade99f198f60d5b654966c` | +| `src/bin/desktop/native_presentation_harness.rs` | `8ce8efa63afbe0d09e22996ec00478de331fcbda7a5ac83882f2ed3fff0d8ea1` | +| `src/bin/desktop/scene_ui.rs` | `8ec81841a2792621205e58d7fdb80078ed75c94b823ddd6cb14976c0decda1a7` | +| `src/bin/desktop/setup_ui_tests.rs` | `278b7a20f5d48a4e8a6c8ee2a4b18f0377f09f78bd64b34cfb20d4545ebc2563` | +| `src/bin/desktop/timeline_ui_tests.rs` | `545e109f6d6808cc31482b8cffb4e21f8518d705424327728dc6c5d549a73df5` | +| `src/bin/desktop/viewport.rs` | `4a365fbb1a0f48bd016b3d673b30e635ad26390205cb22c24d25fa46738b6a08` | +| `src/bin/rheon-desktop.rs` | `d74cff5459f52d1cb01512b6300d9466411352f73b014491e3678d514844d1b3` | + +The appended review, actual complete v3 manifest and execution-contract status +require independent review before atomic commit/publication; approval of the +source-only patch is not approval of unseen admission bytes or native execution. +Fresh native/rational qualification must use the clean committed candidate within +the existing20minute workflow. Existing hosted Rust and full book qualification, +including real packet normal/optimized rejection tests, must bind that actual +source. No receipt refresh, allow-dirty, gate skip, threshold relaxation, budget +expansion or PR merge is admitted by this source-identity record. FEMPR147 remains +outside this change; recheck main/dependencies before integration. diff --git a/docs/education/aligned-strain-native-sources-integration-20261014.json b/docs/education/aligned-strain-native-sources-integration-20261014.json index a0499313..6ed01244 100644 --- a/docs/education/aligned-strain-native-sources-integration-20261014.json +++ b/docs/education/aligned-strain-native-sources-integration-20261014.json @@ -1,13 +1,13 @@ { "schema": "rheon-reviewed-aligned-strain-native-sources-v3", "reviewed_base": "21d4122c3d0edb2ca5c3ce089175c70035d737a3", - "scope": "Explicit reviewed Scene authoring and gated native-carrier/particle-material runtime integration at da5c688, preserving the merged backend 7516a8e force admission; complete 136-path closure with 10 added, 10 changed, 116 unchanged and none removed relative to the merged 126-path admission. Source admission only; production qualification remains Pending, unsupported paths remain refused, and fresh committed-source native/rational and packet/publication checks remain required.", + "scope": "Explicit reviewed production-policy force enabling candidate after fully qualified3ce3f29, preserving all prior backend/UI admission history. Complete136-path selection:11reviewed desktop sources rebound,125unchanged,none added/removed. No equation/schema/threshold/selector changes. New actual-App input/resource protocol remains pending native-owner process/graphics facts and separately reviewed execution; fresh clean committed-source native/rational and full hosted qualification remain required.", "preserved_inventory_sha256": { "docs/education/aligned-strain-native-sources.json": "84c036618b32de66623d8ded24ae8cf95194457e36b9a130087a9c2bf3bd3f6a", "docs/education/aligned-strain-native-sources-runtime-20261013.json": "6ada2133501aab449ed7ee73f802d8a4593db50c857f0a8f8ce2af4507721d6d" }, "review_record": "docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md", - "review_sha256": "3e190beb40264f575a54e36ee33160ae3da73e36fa7032c67702ab8a8f2ff48a", + "review_sha256": "fe11734e336ae2527aa8f96cac81c284da3d50229317a2c1fb5a0c6b79e97510", "source_sha256": { "Cargo.lock": "3b5836a7be9f1290c698cadaff010e11b93e33b4af839c8549391c23e7d8ec0c", "Cargo.toml": "f925dae7a0d74e6024e6e0d64962a275ae4f28fedec5b016341671021b8239ae", @@ -49,18 +49,18 @@ "src/bin/desktop/configured_comparison_tests.rs": "e6716e8c707f30cc20304b16e3201f8fb1fd838650bd08a43a95ab4d68cbbf46", "src/bin/desktop/configured_controls.rs": "5463e18b3b5a139c448dd327b23318605664e90aea4690187981177e7f68e60b", "src/bin/desktop/configured_producer_exit_tests.rs": "7560ff5395acbd50ce111b1e50ae5e8aca1b1bab5356ec195929eec0994f24bb", - "src/bin/desktop/configured_worker_tests.rs": "dabe1c3cf4214ee30ca42102c4c5387564b661f81b810d2c61035b8691669016", + "src/bin/desktop/configured_worker_tests.rs": "06a9e7f5724b73c101a38dcc2c966d37b640900495643ea7bc04c13f4630f278", "src/bin/desktop/emitter_controls.rs": "d5eaf6358b037d1f11177abbbd83bfa8aa18c263b195c3911b8f183b3334fba9", "src/bin/desktop/engineering.rs": "9fc4eaafd1118abda50b70d1cc71c843fc1ca71d420d2cd47f16a20fcc9e8e9d", "src/bin/desktop/export_ui.rs": "1702f437b17dd42d2bf1311ca14d0301a53712392abc6591ab8962b48cba511d", - "src/bin/desktop/force_run_policy.rs": "086d4ee8c18ff30f2165b8622a53d649bd9eb8166bbbbcb82dde3a0eb2427cc2", - "src/bin/desktop/force_run_policy_tests.rs": "a518b47b71cb2aff5d7ce46169918c60d534c00ffccdd0ee5c90abcebbdcdebd", - "src/bin/desktop/force_runtime_integration_tests.rs": "da753230b81df85227ab348c39ea4ac97e2ff093cf2da09ff9ac5dce225d0584", - "src/bin/desktop/force_runtime_ui_tests.rs": "1d242eaed1b416e3dfb2577d5610c4e0d6d0ca5040f80293681ae184090cebb9", + "src/bin/desktop/force_run_policy.rs": "cbb784c1cc403cfd524b715a52144c662f428b302014af1794bb3131d09bb97a", + "src/bin/desktop/force_run_policy_tests.rs": "99d6b9e8b2a98eceab0514510d3f54873da50fdabc7626dd3896d45dd3c53383", + "src/bin/desktop/force_runtime_integration_tests.rs": "04ba4f5883e48c5548cb420e17b6a6736436871d8e6c7792487939bfe5ed29a6", + "src/bin/desktop/force_runtime_ui_tests.rs": "92475c2b577510a8f8fc11217f7eb1a5361dcaef20ade99f198f60d5b654966c", "src/bin/desktop/history.rs": "e4be154bbbf40a36f370b68ec9e15fea663fb589d476a43eb298c0e8c0f0d821", "src/bin/desktop/identity.rs": "df451a986476b9df252a9a4dbc1e33218bf5ab3cb268b83fa78b47dfffdae184", "src/bin/desktop/inspector.rs": "7a6582faf9bfff755d7fade624f6b11cca06813c75310fba25ccbfabff1d2aae", - "src/bin/desktop/native_presentation_harness.rs": "38b8b68ebc8a54769bcf8e65b2cf80890c03eac2251b4f838fac29ecbf63ecb6", + "src/bin/desktop/native_presentation_harness.rs": "8ce8efa63afbe0d09e22996ec00478de331fcbda7a5ac83882f2ed3fff0d8ea1", "src/bin/desktop/particle_view.rs": "75208acd210d4558a34127b98cf77f2c898a560f991f6bed3bee3a4eb160b9a2", "src/bin/desktop/run_set_view.rs": "ab2c841612f39b16f5bd1a363593eae5388bf4a81ec495dfb89e43bd7436580b", "src/bin/desktop/run_sets.rs": "b25f07c5d9f4a27572922956043224c35dde35d9a0d987ab8af4f3f3151c91f0", @@ -68,15 +68,15 @@ "src/bin/desktop/run_sets/configured/archive.rs": "eb1d8e77019eb57afd9e5fd5f57af55f8ff320527ec8efc935945bd7ff477806", "src/bin/desktop/run_sets/source.rs": "39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf", "src/bin/desktop/scene_handles.rs": "4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22", - "src/bin/desktop/scene_ui.rs": "46eeaeaf3cce71c7665cde4224ef2ed7e62100047796414c94d07e02dd4b7390", + "src/bin/desktop/scene_ui.rs": "8ec81841a2792621205e58d7fdb80078ed75c94b823ddd6cb14976c0decda1a7", "src/bin/desktop/scene_view.rs": "d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f", "src/bin/desktop/settings.rs": "72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4", - "src/bin/desktop/setup_ui_tests.rs": "db3d1328aa159e79ea4f8a15cd59ebb6cb7c9352854760bd8164587014e35900", - "src/bin/desktop/timeline_ui_tests.rs": "606b0e95edba4a813221af776e7b12f776adcf9645de302ef827aa595111694b", + "src/bin/desktop/setup_ui_tests.rs": "278b7a20f5d48a4e8a6c8ee2a4b18f0377f09f78bd64b34cfb20d4545ebc2563", + "src/bin/desktop/timeline_ui_tests.rs": "545e109f6d6808cc31482b8cffb4e21f8518d705424327728dc6c5d549a73df5", "src/bin/desktop/transport_view.rs": "e8c3d5cc588d4efac9918544fc5012696a05bdfa7adcba7ce18b07c5a260f6b8", - "src/bin/desktop/viewport.rs": "ee7acacd33ae29af7ec7f592361861315b30891b1637cb2989ba7390fa8320d3", + "src/bin/desktop/viewport.rs": "4a365fbb1a0f48bd016b3d673b30e635ad26390205cb22c24d25fa46738b6a08", "src/bin/rheon-ai-compare.rs": "00000ec93161c04d9ff0515e5ae2ab6855a5467f56f23a4d6fc6c1059ef8ada9", - "src/bin/rheon-desktop.rs": "dfc472b78c46e83b45668a8d99bb82f721fcc771ddc584cdc8fdaf82272003ff", + "src/bin/rheon-desktop.rs": "d74cff5459f52d1cb01512b6300d9466411352f73b014491e3678d514844d1b3", "src/bin/rheon.rs": "5116a121256c8bb57d086caedbda8151f3593cb99d3e84b1d04fea25b12dfb51", "src/box_flux.rs": "554b3cdc916e7527bbfdbd6a54b8cf7e27549c3c661015b259c8b2d18784bbb0", "src/box_flux_step.rs": "e2ef3afcd8adffe0f79c0030eb56e5bb55943775e474a1dab2a7fe78ac38441b", diff --git a/docs/plans/ui-refinement/reports/enabled-force-app-validation-contract.md b/docs/plans/ui-refinement/reports/enabled-force-app-validation-contract.md new file mode 100644 index 00000000..1073dc48 --- /dev/null +++ b/docs/plans/ui-refinement/reports/enabled-force-app-validation-contract.md @@ -0,0 +1,175 @@ +# Production force enabling candidate and actual-App validation + +This follows the fully qualified `3ce3f29cdec0613272064eb8df2f4fbad6ee0603` +checkpoint. Its historical CI, receipts and native property acceptance remain +separate evidence. The candidate enables the shared production policy, removes +all private `force_validation` fields/branches, keeps unsupported-owner refusal, +and projects the same target policy in Scene and Run controls. A force edit must +be applied or cancelled before launch. No solver equation, schema, historical +hash, threshold or scientific admission tool changes. + +## Implementation and review sequence + +1. Review the complete eleven-file desktop patch, including every Request literal, + target precedence, saved None/Some custody, Scene warning and viewport wording. +2. Inspect helpers, enumerate/seal the binary, run only the ten individually exact + metadata/UI cases. Strict desktop all-target Clippy, compile and formatting pass. +3. Append the actual candidate review without rewriting prior reviews. Explicitly + bind all 136 selected sources in the existing v3 inventory; independently review + the complete resulting manifest/review and preserve v1/v2 inventory bindings. +4. Publish a clean committed candidate normally to PR146. Fresh existing hosted + Rust/book/native-rational checks must bind that source; no stale receipt reuse. +5. The native owner supplies the process/graphics facts below. Review its concrete + launcher, limits, owned output roots and disposition before numerical execution. +6. Run the three specified finite App dispatches serially, once each, stopping on + first failure/timeout/bound breach. Keep failures and original artifacts. No + tuning, retry, comparison baseline, second process owner or scientific workload. +7. Independently review source/binary/recording/capture seals and actual outcomes. + Do not merge until final candidate CI and actual-App validation pass. If enabling + validation fails, restore Pending in a new commit; retain the failed evidence. + +## Production executable and common input + +Use the normal production App, not a test executable, synthetic presentation +harness, injected App state or private override. Record exact committed head/tree, +compiler/profile/features, binary SHA256, launch command and OS/graphics facts. +The native property acceptance on da5 is relevant source history, not execution of +this candidate. Graphics/process limits cannot be inferred from pure-test limits. + +Use a 1 m cubic zero-origin domain; one Jacobi track; one requested step at 1e-6 s; +manual numerical threads 1; worker-observation overlay off; no collision model or +emitter. Exact preview edge 2, timeline budget 16 MiB. GUI recording controls: +1 MiB database cap, 1 MiB per-publication cap, requested steps + constructor = 2 +records. Replace the default recording directory with a fresh, absolute, +explicitly owned directory before dispatch. One separate root per dispatch. + +Author exactly one enabled force: id allocated in the fresh document, name +`Desktop custody probe`, identity rotation, translation [0.5,0.5,0.5] m, +Acceleration units, Directional [0.25,0,0] m/s², Global support, activation [0,∞). +Predeclare the expected document from the fresh creation/edit sequence and +capture normal UI values before launch. The production Scene has no raw-id or +canonical-hash export: verify actual allocated id/bytes/hash after dispatch from +the frozen native intent and constructor/accepted records. For C use its recorded +force metadata. Do not claim a prelaunch export, inject App state, substitute a +cached scene or silently create another force. + +## A — fresh native Draft Run, one dispatch + +Select carrier preview and physical density 2 cells/m, producing 2³=8 cells with +0.5 m spacing. Count-mode's widget minimum 4 is not this route. Select saving an +immutable native run set, then click the normal Simulate action. The production +App fixes density 1 kg/m³, retained-array cap 192 MiB, maximum pressure iterations +2000, absolute residual 1e-12, actual divergence limit 1e-5/s and Courant 1. +Leave Tight off: relative residual 1e-9, predicted divergence limit 1e-7/s. +The legacy smoke SourceSpec remains declared with active_steps=0 for this one-step +run; no source is applied. Do not relabel it as the earlier density-2/max100/1-MiB +headless fixture. Maximum planned pressure work is 8×2000 cell-iterations. + +Observe actual App dispatch/progress/completion, one accepted generation, constructor +and accepted record, exact finite dt=1e-6 and the force-qualified pressure interval. +Cold verification checks geometry/density/tolerances, two records, Completed status, +generation 1, native v2 intent digest/manifest association and identical original +force metadata in constructor and accepted records. Also require a predeclared +coarse effect: cell0-gauge pressure at index i lies within0.025 Pa of +(i%2)×0.125 Pa, from rho×ax×dx=1×0.25×0.5. This preserves the earlier20% +relative coarse dispatch guard at the GUI density1; it is not scientific pressure +accuracy validation. Zero-force or metadata-only stepping cannot pass it. + +After completion, change the independent Draft force vector to [0.5,0,0]. Retained +run properties and both cold records must still show the original [0.25,0,0]. No +in-flight mutation or fabricated long run is required to demonstrate this separation. + +## B — native saved-intent rerun, one dispatch + +Open A's completed immutable native run set using the supported UI action, then +Rerun saved set from zero into a second fresh owned recording root. Confirm that +saved intent, not the independently edited Draft, supplies the original force. +The same frozen geometry, density, limits, time horizon and pressure method apply. +This adds exactly one further 8-cell/one-step/max2000 dispatch; total native work +across A+B is at most 32000 planned cell-iterations. A must remain unchanged; +new run identity/archive/started/finished manifests must be distinct. Cold verify +B's constructor + accepted records and exact original force document, including +A's same predeclared pressure-effect guard on B's accepted pressure. This is +constructor-zero rerun, never continuation of accepted fields. + +A separately reviewed saved input could retain density2/max100/1-MiB/64-KiB limits +through this same supported action. That would be a different input contract and +saved-intent evidence, not a replacement claim for fresh Draft Run A. + +## C — fresh material Draft Run, one dispatch + +Return the Draft force through normal UI edits to the exact common [0.25,0,0] +input before C; predeclare the document and capture normal UI values, then verify +its actual id/bytes from C’s records after dispatch. Select material preview, adaptive +numerical grid edge 4, particle samples/side 4, +EmittedFluid fill, Water at293.15 K, Air ambient293.15 K, gravity [0,-1,0] m/s², +liquid sound speed20 m/s, wall heat exchange1/s, particle cap32768. Exact display +preview remains edge2. Keep the one original [0.25,0,0] force, no emitter/model. +The normal GUI's minimum cap32768 and default retained-array cap192 MiB apply. +Initial particle count is 4³=64; assert it from actual recorded data. Exactly one +public step is admitted, with the canonical maximum16 internal preparations and +no external retries. Accepted positive finite dt may shorten; use its actual bits. + +Observe actual App dispatch/progress/completion and cold verify constructor + +accepted records, count64, generation1, recorded configuration/geometry, original +canonical force bytes, actual timing/diagnostics and preview/result association. +After completion change the independent Draft, then recheck the original records +and Retained display. Require finite positive initial mass M and net X momentum +change within[0.5,1.5]×M×0.25×actual_dt, using actual accepted dt. This is the +earlier coarse dispatch-effect guard, not SPH accuracy/equivalence validation. +The retained typed Request and cold force association remain +future-run intent. Material has no serialized cold-rerun API: this case does not +invent one, reconstruct intent from Debug text, or execute a second material run. + +## Source-derived managed resource facts + +These facts do not bound total RSS/address space, renderer or allocator overhead. +For A/B, BufferPlan::for_grid reports two MAC velocity arrays (36faces×8bytes), +two tracer arrays (8cells×8bytes), six pressure vectors (8cells×48bytes):736 bytes. +Each8-byte velocity/tracer term is the pair of f32 arrays, not one f64 array. +Accepted force/pressure metadata, histories, rendering and recording are additional. +The 192-MiB setting is an allocation admission cap, not an observed allocation. + +For C, ParticleFluidSimulation::new sizes accepted/candidate, thermodynamic, +diagnostics, force and next arrays at max_particles, not initial_count. Its exact +source formula is cap×(2×sizeof(FluidParticle)+sizeof(Thermodynamic)+ +sizeof(ParticleDiagnostic)+sizeof(Force)+sizeof(usize)) + bins×sizeof(usize). +For the expected64-bit layouts these components are72,16,40,32,8bytes; cap32768 +therefore requests7864320bytes plus8 bin heads=64bytes. Actual Vec capacities are +checked against192MiB and may exceed nominal requested lengths. This is not the +old cap64/1-MiB fixture. These are source-derived expected layouts, not observed native allocations. The +192-MiB managed cap remains the conservative admitted bound when actual capacities +are not observable. Do not manufacture actual capacity or RSS measurements from +this arithmetic or require a nonexistent GUI export. +Preview history admits only two edge2 frames per single-track dispatch, including +PreviewFrame/Arc overhead; configured16MiB is a limit, not claimed consumption. + +Each database enforces max_page_count for1MiB, DELETE journal and FULL synchronous. +Each connection requests1MiB page cache, mmap_size0, temp_storeFILE and250ms busy +wait. Journal, allocator/connection memory and temporary-file peaks are additional. +Native manifests/specs are individually limited to1MiB. There are at most two +logical publications per archive, each capped1MiB; these are not aggregate disk +quotas. Native save/rerun also publishes immutable intent/started/finished JSON. +Do not claim an observed final inventory is a hard peak/open/anonymous-temp bound. +Preserve output on failure; existing SQLite/atomic-publication protocols may unlink +their own transient files normally, so historical transient retention is not claimed. + +## Required native-owner process contract — unresolved, execution blocked + +Before A/B/C, return actual OS/launcher and existing enforceable wall/CPU/process or +address-space/graphics limits, numerical versus graphics thread accounting, +output/log/capture limits, owned-child identification and termination. Include the +actual SQLite temporary-file location and peak handling; changing the recording +directory alone does not set TMPDIR or SQLITE_TMPDIR. Supply prior +idle-App resource evidence if available, or identify the separately bounded supported +nonnumerical measurement needed. A finite source workload is not a process bound. +Do not invent GUI limits from the30s/20CPU/512MiB/1MiB pure/Worker test envelope. +Do not provision cgroups, mounts, quotas, credentials or security settings. + +State exact wall deadline, available enforcement mechanism, TERM/grace/owned-child +KILL (or actual platform equivalent), and retention/teardown before execution. +Bounds must include three finite dispatches, App/renderer overhead and cold readback; +captures and reviewers are separate process owners and must be accounted honestly. +An unsupported essential limit is a concrete blocker to report, not permission to +redesign containment or silently expand an envelope. No new numerical execution is +admitted while these fields and the independent actual contract review are pending. diff --git a/src/bin/desktop/configured_worker_tests.rs b/src/bin/desktop/configured_worker_tests.rs index 04521869..036fd615 100644 --- a/src/bin/desktop/configured_worker_tests.rs +++ b/src/bin/desktop/configured_worker_tests.rs @@ -486,8 +486,6 @@ fn actual_runtime_failure_terminal_is_not_relabelled_by_late_worker_cancel() { let (release, resume) = std::sync::mpsc::sync_channel(0); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 2, steps: 3, diff --git a/src/bin/desktop/force_run_policy.rs b/src/bin/desktop/force_run_policy.rs index 88cd5c19..2d33da9a 100644 --- a/src/bin/desktop/force_run_policy.rs +++ b/src/bin/desktop/force_run_policy.rs @@ -10,8 +10,9 @@ pub enum Qualification { Pending, Validated, } -// Remains closed until the exact native acceptance is returned and reviewed. -pub const PRODUCTION_QUALIFICATION: Qualification = Qualification::Pending; +// Shared production/test admission after reviewed native interaction and custody +// checkpoints. The enabling candidate still requires actual-App validation before merge. +pub const PRODUCTION_QUALIFICATION: Qualification = Qualification::Validated; pub const PENDING: &str = "Authored forces cannot run yet: native interaction acceptance is pending"; pub const UNSUPPORTED: &str = "Authored forces are unsupported for this workflow (configured/session/provider/free-surface/AI)"; @@ -29,8 +30,9 @@ pub fn check( if target == Target::Unsupported { return Err(UNSUPPORTED); } - if qualification != Qualification::Validated { - return Err(PENDING); + if qualification == Qualification::Pending { + Err(PENDING) + } else { + Ok(()) } - Ok(()) } diff --git a/src/bin/desktop/force_run_policy_tests.rs b/src/bin/desktop/force_run_policy_tests.rs index d563c0be..6cffeace 100644 --- a/src/bin/desktop/force_run_policy_tests.rs +++ b/src/bin/desktop/force_run_policy_tests.rs @@ -23,7 +23,6 @@ fn fields() -> rheon::ForceSnapshot { fn request() -> Request { Request { force_fields: Vec::new(), - force_validation: false, collision_mesh: None, size: 2, steps: 1, @@ -70,7 +69,7 @@ fn unsupported_request() -> ConfiguredRequest { } #[test] fn force_policy_qualification_never_grants_unsupported_owner() { - assert_eq!(PRODUCTION_QUALIFICATION, Qualification::Pending); + assert_eq!(PRODUCTION_QUALIFICATION, Qualification::Validated); for qualification in [Qualification::Pending, Qualification::Validated] { assert_eq!( check(Target::Unsupported, true, qualification), @@ -85,7 +84,8 @@ fn force_policy_qualification_never_grants_unsupported_owner() { } } for target in [Target::NativeCarrier, Target::ParticleMaterial] { - assert_eq!(check(target, true, PRODUCTION_QUALIFICATION), Err(PENDING)); + assert_eq!(check(target, true, Qualification::Pending), Err(PENDING)); + assert_eq!(check(target, true, PRODUCTION_QUALIFICATION), Ok(())); assert_eq!(check(target, true, Qualification::Validated), Ok(())); } } @@ -109,8 +109,6 @@ fn force_policy_request_and_setup_resolve_effective_targets() { let mut r = request(); r.force_fields = vec![Some(snapshot)]; assert_eq!(r.force_target(), Target::NativeCarrier); - assert_eq!(validate_request(&r), Err(PENDING.to_owned())); - r.force_validation = true; assert!(validate_request(&r).is_ok()); r.material = Some(Default::default()); assert_eq!(r.force_target(), Target::ParticleMaterial); @@ -122,8 +120,6 @@ fn force_policy_request_and_setup_resolve_effective_targets() { "configured takes precedence over material" ); assert_eq!(validate_request(&r), Err(UNSUPPORTED.to_owned())); - r.force_validation = false; - assert_eq!(validate_request(&r), Err(UNSUPPORTED.to_owned())); r.methods = PressureImplementation::ALL.to_vec(); r.force_fields.push(r.force_fields[0].clone()); assert_eq!( @@ -138,12 +134,27 @@ fn force_policy_scene_readiness_uses_valid_draft_not_retained_display() { let fields = fields(); let draft = scene_ui::SceneUi::geometry_fixture(fields.scene().clone(), false); for target in [Target::NativeCarrier, Target::ParticleMaterial] { - assert_eq!(draft.launch_readiness(target), Err(PENDING.to_owned())); + assert_eq!(draft.launch_readiness(target), Ok(())); } assert_eq!( draft.launch_readiness(Target::Unsupported), Err(UNSUPPORTED.to_owned()) ); + let mut editing = scene_ui::SceneUi::geometry_fixture(fields.scene().clone(), false); + let original = serde_json::to_vec(editing.draft()).unwrap(); + editing.select(Some(rheon::SceneObjectId(1))); + assert!(editing.begin_edit()); + let mut preview = fields.scene().objects[0].clone(); + preview.transform.translation[0] = 0.25; + assert!(editing.preview_edit(Some(preview))); + assert_eq!(serde_json::to_vec(editing.draft()).unwrap(), original); + assert_eq!( + editing.launch_readiness(Target::NativeCarrier), + Err("Finish or cancel the force edit before running".into()) + ); + editing.finish_edit(false); + assert_eq!(serde_json::to_vec(editing.draft()).unwrap(), original); + assert_eq!(editing.launch_readiness(Target::NativeCarrier), Ok(())); let mut invalid = fields.scene().clone(); invalid.objects[0].support = rheon::ForceSupport::Sphere { radius: 0.0, @@ -164,3 +175,26 @@ fn force_policy_scene_readiness_uses_valid_draft_not_retained_display() { assert_eq!(empty.launch_readiness(Target::Unsupported), Ok(())); assert_eq!(empty.launch_readiness(Target::NativeCarrier), Ok(())); } + +#[test] +fn force_policy_supported_requests_keep_budget_and_material_track_guards() { + let mut r = request(); + r.force_fields = vec![Some(fields())]; + assert!(validate_request(&r).is_ok()); + r.history_budget_mib = 0; + assert_eq!( + validate_request(&r), + Err("Invalid timeline memory budget".into()) + ); + r.history_budget_mib = 16; + r.threads = 0; + assert!(validate_request(&r).unwrap_err().contains("CPU threads")); + r.threads = 1; + r.material = Some(Default::default()); + r.methods = PressureImplementation::ALL.to_vec(); + r.force_fields.push(r.force_fields[0].clone()); + assert_eq!( + validate_request(&r), + Err("Authored particle material requires exactly one material track".into()) + ); +} diff --git a/src/bin/desktop/force_runtime_integration_tests.rs b/src/bin/desktop/force_runtime_integration_tests.rs index 70581eb8..6e094f6d 100644 --- a/src/bin/desktop/force_runtime_integration_tests.rs +++ b/src/bin/desktop/force_runtime_integration_tests.rs @@ -57,7 +57,6 @@ fn output_root() -> PathBuf { fn request(root: &Path, fields: &ForceSnapshot) -> Request { Request { force_fields: vec![Some(fields.clone())], - force_validation: true, collision_mesh: None, size: 2, steps: 1, diff --git a/src/bin/desktop/force_runtime_ui_tests.rs b/src/bin/desktop/force_runtime_ui_tests.rs index b5a6681a..e11b2d1f 100644 --- a/src/bin/desktop/force_runtime_ui_tests.rs +++ b/src/bin/desktop/force_runtime_ui_tests.rs @@ -221,7 +221,6 @@ fn force_intent_rejects_mixed_versions_tamper_unknown_duplicate_and_oversize() { fn request() -> Request { Request { force_fields: Vec::new(), - force_validation: false, collision_mesh: None, size: 2, steps: 1, @@ -255,11 +254,6 @@ fn force_request_track_resolver_gates_before_work_and_binds_saved_members() { let fields = scene().freeze().unwrap(); let mut r = request(); r.force_fields = vec![Some(fields.clone())]; - assert_eq!( - validate_request(&r), - Err(force_run_policy::PENDING.to_owned()) - ); - r.force_validation = true; assert!(validate_request(&r).is_ok()); let forced = spec().with_force_fields(&fields).unwrap(); r.methods = PressureImplementation::ALL.to_vec(); @@ -281,7 +275,7 @@ fn force_request_track_resolver_gates_before_work_and_binds_saved_members() { r.force_fields = vec![Some(fields)]; assert!(validate_request(&r).unwrap_err().contains("track count")); r.force_fields.clear(); - r.force_validation = false; + assert!(validate_request(&r).is_ok()); } #[test] @@ -333,7 +327,15 @@ fn force_retained_track_projection_is_readonly_and_clears_same_id_selection() { assert_eq!(ui.selected(), None); let ctx = egui::Context::default(); ui.open = true; - let out = ctx.run(Default::default(), |ctx| ui.show(ctx, true, false, None)); + let out = ctx.run(Default::default(), |ctx| { + ui.show( + ctx, + true, + false, + None, + force_run_policy::Target::NativeCarrier, + ) + }); assert!( out.shapes.iter().any( |s| matches!(&s.shape,egui::Shape::Text(t)if t.galley.text().contains("read-only")) diff --git a/src/bin/desktop/native_presentation_harness.rs b/src/bin/desktop/native_presentation_harness.rs index 853855e5..b6529757 100644 --- a/src/bin/desktop/native_presentation_harness.rs +++ b/src/bin/desktop/native_presentation_harness.rs @@ -13,8 +13,6 @@ fn style(ctx: &egui::Context) { fn fixture() -> App { let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 2, steps: 1, @@ -130,7 +128,13 @@ impl Presentation { } let retained_view = self.app.prepare_scene_presentation(); let previous = self.app.scene.context; - self.app.scene.show(ctx, true, false, None); + self.app.scene.show( + ctx, + true, + false, + None, + force_run_policy::Target::NativeCarrier, + ); if previous != self.app.scene.context { self.app.playing = false; } diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index 3903e124..4e006061 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -269,6 +269,9 @@ impl SceneUi { }); } pub fn launch_readiness(&self, target: super::force_run_policy::Target) -> Result<(), String> { + if self.editing() { + return Err("Finish or cancel the force edit before running".into()); + } self.draft() .freeze() .map_err(|error| format!("Invalid authored force: {error}"))?; @@ -322,6 +325,7 @@ impl SceneUi { has_run: bool, active_worker: bool, model: Option, + target: super::force_run_policy::Target, ) { if !self.open { egui::SidePanel::right("scene-collapsed") @@ -343,9 +347,10 @@ impl SceneUi { }); if previous!=self.context {self.selected=None;} let editable=self.context==SceneContext::Draft && !active_worker && !self.editing(); - if self.context==SceneContext::Draft && !self.forces.objects.is_empty() { + if self.context==SceneContext::Draft && !self.forces.objects.is_empty() + && let Err(error)=self.launch_readiness(target) { ui.colored_label(egui::Color32::YELLOW,"Authored-force runs unavailable") - .on_hover_text("Runtime and archive integration is pending. No authored force will be silently ignored."); + .on_hover_text(error); } if self.context==SceneContext::Draft && let Some(error)=&self.error {ui.colored_label(egui::Color32::LIGHT_RED,error);} egui::ScrollArea::vertical().id_salt("scene-properties-scroll").show(ui,|ui| { @@ -542,7 +547,13 @@ mod tests { .exact_width(280.0) .show(ctx, |_| {}); } - scene.show(ctx, true, false, None); + scene.show( + ctx, + true, + false, + None, + super::super::force_run_policy::Target::NativeCarrier, + ); available = ctx.available_rect(); }, ); @@ -950,7 +961,7 @@ mod tests { ); } #[test] - fn scene_force_refusal_projects_disabled_run_without_dispatch() { + fn scene_force_policy_projects_supported_and_refused_intents_without_dispatch() { // Paint/capture intent only: even a broken guard cannot start work here. fn project( ctx: &egui::Context, @@ -975,36 +986,81 @@ mod tests { ); (output, requested) } + use super::super::SetupWorkflow; + for (material, workflow, invalid, expected) in [ + (false, SetupWorkflow::Preview, false, true), + (true, SetupWorkflow::Preview, false, true), + (false, SetupWorkflow::AcceptedSession, false, false), + (true, SetupWorkflow::AcceptedSession, false, false), + (false, SetupWorkflow::Preview, true, false), + ] { + let ctx = context(); + let mut app = super::super::App::default(); + app.material_mode = material; + app.setup_workflow = workflow; + app.scene.add(NewForce::Directional).unwrap(); + if invalid { + app.scene.forces.objects[0].support = ForceSupport::Sphere { + radius: 0.0, + edge_fraction: 0.0, + }; + } + let (output, requested) = project(&ctx, &mut app, vec![]); + assert!(!requested); + let unavailable = output.shapes.iter().any(|s|matches!(&s.shape,egui::Shape::Text(t) if t.galley.text()=="Authored-force runs unavailable")); + assert_eq!(unavailable, !expected); + let label = match workflow { + SetupWorkflow::Preview => "Simulate", + SetupWorkflow::AcceptedSession => "Save & run native", + }; + let pos = text_rect(&output, label, false).center(); + let mut captured = false; + for pressed in [true, false] { + let (_, requested) = project( + &ctx, + &mut app, + vec![ + egui::Event::PointerMoved(pos), + egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed, + modifiers: Default::default(), + }, + ], + ); + captured |= requested; + } + assert_eq!(captured, expected); + assert!(app.worker.is_none() && app.history.is_none() && app.running_request.is_none()); + } + } + #[test] + fn scene_force_warning_projects_effective_target_without_dispatch() { + use super::super::force_run_policy::Target; let ctx = context(); - let mut app = super::super::App::default(); - app.scene.add(NewForce::Directional).unwrap(); - let (output, requested) = project(&ctx, &mut app, vec![]); - assert!(!requested); - assert!(output.shapes.iter().any(|s|matches!(&s.shape,egui::Shape::Text(t) if t.galley.text()=="Authored-force runs unavailable"))); - let pos = text_rect(&output, "Simulate", false).center(); - for pressed in [true, false] { - let (_, requested) = project( - &ctx, - &mut app, - vec![ - egui::Event::PointerMoved(pos), - egui::Event::PointerButton { - pos, - button: egui::PointerButton::Primary, - pressed, - modifiers: Default::default(), - }, - ], + let mut scene = SceneUi { + open: true, + ..Default::default() + }; + scene.add(NewForce::Directional).unwrap(); + for (target, unavailable) in [ + (Target::NativeCarrier, false), + (Target::ParticleMaterial, false), + (Target::Unsupported, true), + ] { + let output = ctx.run( + egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(900.0, 650.0), + )), + ..Default::default() + }, + |ctx| scene.show(ctx, false, false, None, target), ); - assert!(!requested); + assert_eq!(output.shapes.iter().any(|s| matches!(&s.shape,egui::Shape::Text(t) if t.galley.text()=="Authored-force runs unavailable")), unavailable); } - assert!(app.worker.is_none() && app.history.is_none() && app.running_request.is_none()); - app.scene.forces.objects[0].enabled = false; - assert!( - app.scene - .launch_readiness(super::super::force_run_policy::Target::NativeCarrier) - .is_err() - ); } #[test] fn scene_draft_timeline_roundtrip_preserves_retained_index_without_history() { diff --git a/src/bin/desktop/setup_ui_tests.rs b/src/bin/desktop/setup_ui_tests.rs index 3c54d610..91f6faa4 100644 --- a/src/bin/desktop/setup_ui_tests.rs +++ b/src/bin/desktop/setup_ui_tests.rs @@ -372,8 +372,6 @@ fn ui_setup_running_summary_uses_frozen_physical_samples_and_timestep() { }; let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 32, steps: 37, @@ -625,8 +623,6 @@ fn ui_setup_synthetic_active_worker_keeps_progress_frozen_inputs_and_cancel_sign app.requested_dt = 0.001; app.running_request = Some(Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 4, steps: 4, @@ -764,8 +760,6 @@ fn bounded_genuine_cancel_contract() { let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 4, steps: 2, diff --git a/src/bin/desktop/timeline_ui_tests.rs b/src/bin/desktop/timeline_ui_tests.rs index 629e8fb6..3c6eb958 100644 --- a/src/bin/desktop/timeline_ui_tests.rs +++ b/src/bin/desktop/timeline_ui_tests.rs @@ -248,8 +248,6 @@ fn timeline_controls_retain_transitions_and_physical_time() { fn inert_carrier_request() -> Request { Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 4, steps: 4, diff --git a/src/bin/desktop/viewport.rs b/src/bin/desktop/viewport.rs index ada3afee..24d84d2d 100644 --- a/src/bin/desktop/viewport.rs +++ b/src/bin/desktop/viewport.rs @@ -57,14 +57,15 @@ fn view_mode_tabs(ui: &mut egui::Ui, current: &mut ViewMode, blocked: bool) -> [ [(ViewMode::Appearance,"Appearance"),(ViewMode::Particles,"Particles"),(ViewMode::Obstacle,"Obstacle lab"),(ViewMode::Transport,"Transport session")].map(|(mode,label)|{ let mut response=ui.add_enabled(!blocked||mode==ViewMode::Appearance,egui::Button::selectable(*current==mode,label).wrap_mode(egui::TextWrapMode::Extend)); if response.clicked() && *current!=mode {*current=mode;response.mark_changed();} - response.on_disabled_hover_text("Authored-force preview and lab runs are not integrated. Use the scene list and properties to author the draft.") + response.on_disabled_hover_text("Authored-force scenes use Appearance here. These alternate lab/session views do not consume the run-bound force document.") }) } pub struct Viewport { #[cfg(test)] presentation_only: bool, scene_handles: super::scene_handles::SceneHandles, - /// Draft authored fields have no runtime/preview integration yet. + /// Authored scenes keep alternate lab/session views unavailable; Appearance + /// projects the supported carrier or material preview. pub authored_forces_pending: bool, pub collision_mesh: Option>, pub camera: OrbitCamera, diff --git a/src/bin/rheon-desktop.rs b/src/bin/rheon-desktop.rs index 7a5119a0..2d8bc9e0 100644 --- a/src/bin/rheon-desktop.rs +++ b/src/bin/rheon-desktop.rs @@ -101,8 +101,6 @@ use std::time::{Duration, Instant}; struct Request { /// Empty means legacy on every track; otherwise one immutable option per track. force_fields: Vec>, - #[cfg(test)] - force_validation: bool, collision_mesh: Option>, size: u64, steps: usize, @@ -311,13 +309,6 @@ fn validate_request(request: &Request) -> Result<(), String> { } } } - #[cfg(test)] - let qualification = if request.force_validation { - force_run_policy::Qualification::Validated - } else { - force_run_policy::PRODUCTION_QUALIFICATION - }; - #[cfg(not(test))] let qualification = force_run_policy::PRODUCTION_QUALIFICATION; force_run_policy::check(target, authored, qualification).map_err(str::to_owned)?; @@ -1810,8 +1801,6 @@ impl App { let intent_path = root.join("intent.json"); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: baseline.constructor.counts_xyz[0], steps: baseline.future.requested_steps, @@ -1971,8 +1960,6 @@ impl App { force_fields: fields.map_or_else(Vec::new, |fields| { vec![Some(fields); if all { 2 } else { 1 }] }), - #[cfg(test)] - force_validation: false, collision_mesh: self.collision.mesh.clone(), size, steps: self.steps, @@ -2038,8 +2025,6 @@ impl App { } let mut request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: baseline.counts_xyz[0], steps: baseline.requested_steps, @@ -2684,6 +2669,7 @@ impl eframe::App for App { self.running_request.is_some(), self.worker.is_some(), model, + self.setup_workflow.force_target(self.material_mode), ); if previous_context != self.scene.context { self.playing = false; @@ -3252,8 +3238,6 @@ mod tests { fn material_worker_accepts_and_captures_a_genuine_256_cube_preview() { let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 256, steps: 1, @@ -3474,8 +3458,6 @@ mod tests { let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 32, steps: 64, @@ -3538,8 +3520,6 @@ mod tests { let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 32, steps: 3, @@ -3628,8 +3608,6 @@ mod tests { let (release, resume) = std::sync::mpsc::sync_channel(1); let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: app.size, steps: app.steps, @@ -3718,8 +3696,6 @@ mod tests { fn worker_runs_selected_and_equal_input_comparison() { let request = Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 8, steps: 4, @@ -3818,8 +3794,6 @@ mod tests { run( Request { force_fields: Vec::new(), - #[cfg(test)] - force_validation: false, collision_mesh: None, size: 8, steps: 4, From d5420adef25f4f8e8caa671335fdfb06f2832f42 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 21 Oct 2026 04:41:15 +0000 Subject: [PATCH 25/25] Align Scene interaction contract with enabled force policy --- .../NATIVE_ADMISSION_INTEGRATION_REVIEW.md | 57 +++++++++++++++++++ ...n-native-sources-integration-20261014.json | 6 +- src/bin/desktop/scene_ui.rs | 11 ++-- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md index 9dabdb9c..417981dc 100644 --- a/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md +++ b/docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md @@ -1377,3 +1377,60 @@ including real packet normal/optimized rejection tests, must bind that actual source. No receipt refresh, allow-dirty, gate skip, threshold relaxation, budget expansion or PR merge is admitted by this source-identity record. FEMPR147 remains outside this change; recheck main/dependencies before integration. + +## Reviewed Pending-era Scene test assertion repair after 21e70bd + +The entire preceding review is preserved byte-for-byte from +`21e70bdeb42f529b63ee3cab5a08331c09646c9f`: 92814 bytes, SHA256 +`fe11734e336ae2527aa8f96cac81c284da3d50229317a2c1fb5a0c6b79e97510`. Its v3 manifest SHA256 remains +`3a2e70e515adc69ac3ce5b2713538bb0096c85505e8ac0f0142de0611fd7ba3b` in Git history. +The new repair does not reinterpret the fully qualified3ce checkpoint or21e's +fresh dedicated qualification receipts as receipts for the next source revision. + +Actual21e Rust run39543779071 desktop job119037237201 failed its contract step: +`scene_ui::tests::scene_pointer_add_select_edit_collapse_and_retained_context` +still asserted NativeCarrier readiness was an error at scene_ui.rs741. The actual +checkout ccd3514e431f20aa7a53806ed90462c56f44b0c2 has the exact21e tree +cfadc610a1299a4e1fc84d68dba2ed3daeb4f7c6. Its desktop result was203passed, +1failed,4ignored; it stopped before integration targets. Strict lints had passed. +Original logs,21e receipts and the prior sealed local binary remain preserved. + +The exact minimal source patch SHA256 +`cbe01f17a79d9c9367c532778d4380865352dda214ce60aa4d4d068bf1ee7d90` +replaces this Pending-era expectation with exact NativeCarrier `Ok(())` and exact +Unsupported `Err(UNSUPPORTED.into())`. The valid scene contains three authored +objects after normal egui edits, with its Vortex disabled. Authored presence stays +unsupported for the unsupported owner; successful supported readiness now matches +the intentionally enabled shared production policy. This is a changed eligibility +contract assertion, not weakened numerical validation or acceptance of any error. + +Only assertions inside `#[cfg(test)] mod tests` change. The production prefix of +scene_ui.rs before that module is byte-identical to21e, SHA256 +`0e952e9d717d4b0ed9e6491d53ca334cf386d78d885fee33864aeade5a88d746`. No production control, solver, evaluator, recording, force-scene, +App input protocol, threshold, selector or qualification tool changes. The existing +actual-App contract remains SHA256 +`80043585fd578cd625d5b2a24b4b66173fd852eed9643a50d82338a0908f0d10`. +Native process/graphics/SQLite/output/termination review and A/B/C execution remain +separate and pending; no numerical App work was duplicated here. + +Independent review inspected the actual patch and complete pure helper closure: +SceneUi, canonical metadata and egui context/frame/click/key/replace only, widths +900/1250. No App/Worker, solver/evaluator, filesystem, recorder, model or subprocess +helper is reached. The newly compiled sealed test binary SHA256 +`50b03c3df9af47e943c2f502f3d7835336d3d7c4786fda6daf4787cbd4bc2dfb` +enumerated208unique tests. The exact failing case passed once,1passed/207filtered, +exit0, no signals, under30s wall/20sCPU/512MiBAS/1MiBfile+log, TERM then +owned-child KILL after1s grace, testthreads1 and no parallel feature pool. Its log +SHA256 is `cfd66d3415f86438f655afefa87eb3c71540bb40926fbe78cb1eadfe8bc75a5f`. +Compile-only, strict desktop all-target Clippy and formatting passed. No broad local +tests, scientific L1/MMS or numerical/SQLite workloads were executed for this repair. + +The complete conservative inventory remains136paths: only +`src/bin/desktop/scene_ui.rs` changes to SHA256 +`e98c8aa0449b8f1adac4eb8a5bdc9a0951bd0b054f4dd2575d31dee30990c922`; +135unchanged pins were checked against actual files and the21e inventory; none +added or removed. Historical v1/v2 bindings and review bytes remain intact. The +actual complete three-file candidate requires independent review before atomic +commit/publication, then fresh clean committed-source existing native/rational +and normal Rust/book qualification. No stale receipt refresh, dirty-source bypass, +threshold relaxation, workflow expansion, merge or FEMPR147 changes are admitted. diff --git a/docs/education/aligned-strain-native-sources-integration-20261014.json b/docs/education/aligned-strain-native-sources-integration-20261014.json index 6ed01244..82cb277e 100644 --- a/docs/education/aligned-strain-native-sources-integration-20261014.json +++ b/docs/education/aligned-strain-native-sources-integration-20261014.json @@ -1,13 +1,13 @@ { "schema": "rheon-reviewed-aligned-strain-native-sources-v3", "reviewed_base": "21d4122c3d0edb2ca5c3ce089175c70035d737a3", - "scope": "Explicit reviewed production-policy force enabling candidate after fully qualified3ce3f29, preserving all prior backend/UI admission history. Complete136-path selection:11reviewed desktop sources rebound,125unchanged,none added/removed. No equation/schema/threshold/selector changes. New actual-App input/resource protocol remains pending native-owner process/graphics facts and separately reviewed execution; fresh clean committed-source native/rational and full hosted qualification remain required.", + "scope": "Reviewed cfg(test)-only Scene readiness assertion repair after production enabling21e: exact supported success and unsupported-owner refusal, preserving all historical reviews. Complete136-path inventory:1reviewed test source rebound,135unchanged,none added/removed. Production bytes,App contract,tools,selectors,thresholds and v1/v2 bindings unchanged. Fresh clean committed-source qualification required; actual-App execution remains separately bounded and pending native-owner review.", "preserved_inventory_sha256": { "docs/education/aligned-strain-native-sources.json": "84c036618b32de66623d8ded24ae8cf95194457e36b9a130087a9c2bf3bd3f6a", "docs/education/aligned-strain-native-sources-runtime-20261013.json": "6ada2133501aab449ed7ee73f802d8a4593db50c857f0a8f8ce2af4507721d6d" }, "review_record": "docs/education/NATIVE_ADMISSION_INTEGRATION_REVIEW.md", - "review_sha256": "fe11734e336ae2527aa8f96cac81c284da3d50229317a2c1fb5a0c6b79e97510", + "review_sha256": "32e81e49279b1a0f0fb7f0f35d3264f504099a856d8c09375af3215b6d424e9a", "source_sha256": { "Cargo.lock": "3b5836a7be9f1290c698cadaff010e11b93e33b4af839c8549391c23e7d8ec0c", "Cargo.toml": "f925dae7a0d74e6024e6e0d64962a275ae4f28fedec5b016341671021b8239ae", @@ -68,7 +68,7 @@ "src/bin/desktop/run_sets/configured/archive.rs": "eb1d8e77019eb57afd9e5fd5f57af55f8ff320527ec8efc935945bd7ff477806", "src/bin/desktop/run_sets/source.rs": "39e8982fffaf4e9adcd0ca0c60f49eabc96b829957bc6d9c05479c80eee99daf", "src/bin/desktop/scene_handles.rs": "4818b85644a2891de562adc00fc203eb59c674bbf46aa88cd440bcab6477eb22", - "src/bin/desktop/scene_ui.rs": "8ec81841a2792621205e58d7fdb80078ed75c94b823ddd6cb14976c0decda1a7", + "src/bin/desktop/scene_ui.rs": "e98c8aa0449b8f1adac4eb8a5bdc9a0951bd0b054f4dd2575d31dee30990c922", "src/bin/desktop/scene_view.rs": "d16851804c3aa727c7fdf658a821ef8c8cd2c167e853b72d02604f2a14dfa02f", "src/bin/desktop/settings.rs": "72a2e42de38100fa7cb517bfa7c1b9377975d23c3c4cd887615c296549c093c4", "src/bin/desktop/setup_ui_tests.rs": "278b7a20f5d48a4e8a6c8ee2a4b18f0377f09f78bd64b34cfb20d4545ebc2563", diff --git a/src/bin/desktop/scene_ui.rs b/src/bin/desktop/scene_ui.rs index 4e006061..9d143534 100644 --- a/src/bin/desktop/scene_ui.rs +++ b/src/bin/desktop/scene_ui.rs @@ -738,10 +738,13 @@ mod tests { let (output, _) = frame(&ctx, &mut scene, width, vec![]); click(&ctx, &mut scene, width, &output, "Enabled", false); assert!(!scene.forces.objects[2].enabled); - assert!( - scene - .launch_readiness(super::super::force_run_policy::Target::NativeCarrier) - .is_err() + assert_eq!( + scene.launch_readiness(super::super::force_run_policy::Target::NativeCarrier), + Ok(()) + ); + assert_eq!( + scene.launch_readiness(super::super::force_run_policy::Target::Unsupported), + Err(super::super::force_run_policy::UNSUPPORTED.into()) ); let (output, _) = frame(&ctx, &mut scene, width, vec![]); click(&ctx, &mut scene, width, &output, "Hide scene", false);