Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5b803fa
Phase 7: the GUI step plan and its measured premises
ejams1 Oct 19, 2026
21e5d1e
Phase 7 starts: README progress row tracks the draft
ejams1 Oct 19, 2026
aeb8c7e
Phase 7: the recon findings, the corrected menu premise and the dialo…
ejams1 Oct 19, 2026
ac25ab6
Phase 7 step 1: the xedit-gui crate, the command bridge and the main …
ejams1 Oct 19, 2026
871a37b
xtask: the `gui` screen-check tooling for the phase-7 gate
ejams1 Oct 19, 2026
c467f91
Phase 7 step 1b: the command-layer gaps the GUI dialogs' data sources…
ejams1 Oct 19, 2026
40b3395
Phase 7 step 5: the six selection dialogs
ejams1 Oct 19, 2026
5da64a6
Phase 7 step 2: the navigation tree and everything it drives
claude Oct 19, 2026
88ca7fe
Phase 7 step 2: the PLAN note's test count at the rebased tip
claude Oct 19, 2026
8d4cc51
Phase 7 step 2: the note records the flaky script timing test
claude Oct 19, 2026
c95e249
Phase 7 step 6: the viewer and editor dialogs
ejams1 Oct 19, 2026
6a8064d
Phase 7 gate: the GUI captures its own frame and idles without repain…
ejams1 Oct 19, 2026
61666bc
Phase 7 step 3: the view tab record grid, its folds, colours and edits
ejams1 Oct 19, 2026
f088cb8
Phase 7 step 4: the record panes and the element detail editor
ejams1 Oct 19, 2026
8ebf1e0
Phase 7 step 7: the settings and modal tools, the wait form and the j…
ejams1 Oct 19, 2026
61faf9d
Phase 7: integrate steps 3, 4 and 7 on the phase branch
ejams1 Oct 20, 2026
f52d51f
Phase 7: fix the view tab's element detail panel and filter strip
ejams1 Oct 20, 2026
2cd125f
Update the README for the GUI dialogs of steps 5 to 7
ejams1 Oct 20, 2026
a91b98f
Turn off egui's bundled fonts and honour the dry run of script.save
ejams1 Oct 20, 2026
8b5c57b
Add the script CLI subcommands, the ported forms in the upstream map …
ejams1 Oct 20, 2026
f283f14
Phase 7 step 8: the menu and binding audit
ejams1 Oct 20, 2026
cc7afe6
WIP: the nav flow model and the two prompt dialogs
ejams1 Oct 20, 2026
1570cc7
Phase 7: the nav flow shell, the View Elements pages and the re-synce…
ejams1 Oct 21, 2026
acb4304
Phase 7: drop the stale step machinery, the poll points of a script r…
ejams1 Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .claude/skills/checking-parity/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,68 @@ A whole-file dump of `Fallout4.esm` (`-FO4 -q`) wrote 12.4 GB in 1 h 53 min and

The `xEdit-llm` automation build is a secondary oracle for conflict, reference and cleaning results as JSON. It is a fork at a different upstream commit: use it to cross-check, never to close a gate.

## Screen checks (`cargo xtask gui`)

The phase-7 gate witnesses a GUI claim with a screenshot or a scripted screen interaction against the 4.1.5q binary. `cargo xtask gui` starts a program on a private desktop of the current window station, which is never shown: nothing it draws reaches the user's desktop. A **session** is a JSON file; a helper process (`gui session`, started by `launch` and run from a copy of `xtask`) creates the desktop, starts the program on it, holds the desktop open and serves the other commands through request files beside the session. Windows belong to their desktop, so every list, capture, key, click, menu and close request is executed by that helper, not by the command's own process.

| Command | What it does |
| --- | --- |
| `gui launch <exe> [--args <arg>]... [--desktop <name>] [--session <file>] [--wait <seconds>] [--json]` | Starts the program, waits for its first visible window, writes the session (default `$XEDIT_GUI_SESSIONS`, else `$XEDIT_PARITY_SCRATCH/gui`, else `<cwd>/.xtask-gui/session.json`). A program that exits during startup fails the launch with its exit code. |
| `gui windows <session> [--json]` | Every top-level window of the desktop: handle, pid, class, title, rect, visible, and whether it belongs to the session's program. |
| `gui shot <session> (--title <substr>|--handle <hwnd>)... --out <png> [--json]` | Captures every matching window with `PrintWindow` (BitBlt fallback), writes PNGs. Several matches write `<stem>-<handle>.png`. |
| `gui keys <session> (--title|--handle) --keys "ctrl+s,enter,text:FO4" [--control <class>] [--delay <ms>]` | Posts keys with `WM_KEYDOWN`/`WM_CHAR`/`WM_KEYUP`, modifiers in the target thread's keyboard state. `alt+f` sends `SC_KEYMENU` (a posted Alt+letter never opens a menu). `--control` names the control class that receives the keys (`TLabeledEdit`, `TVirtualStringTree`): **a dialog on a private desktop has no focused control**, so without it the keys go to the form and text is lost. |
| `gui click <session> (--title|--handle) --at <x>,<y> [--button left|right|middle]` | Posts a mouse click at a client point, passed down to the deepest child under it. |
| `gui click <session> (--title|--handle) --click-button <caption>` | Finds a `TButton`/`Button`/`TSpeedButton` child by caption (an `&` is ignored) and sends `BM_CLICK`. Use it where the dialog is DPI-clipped and the button is outside the visible client area. |
| `gui menus <session> (--title|--handle) [--json]` | The window's Win32 menu tree (`GetMenu`/`GetMenuItemInfoW`): text, id, enabled, checked, nested submenus. |
| `gui close <session> [--timeout <seconds>]` | `WM_CLOSE` to the program's windows, then `TerminateProcess` on exactly that pid (checked by its creation time), then ends the helper and removes the session. |

Notes paid for on the first run (2026-10-19):

- The helper runs from a **copy** of `xtask` beside the session, so a rebuild while a session runs is safe, but a command whose protocol changed needs a **new session** (close and launch again).
- `xEdit 4.1.5q` has **no Win32 menu**: its File/View/... menu is `pmuMain`, a popup menu on the `bnMainMenu` hamburger speed button (`Caption = #926`), a `TGraphicControl` with no window handle. `gui menus` therefore answers `has no menu` for it, and `GetMenu` cannot be used for the walkthrough. The button is disabled until a file is loaded; the gate has to open the popup from the main form after loading and capture the `#32768` menu window.
- At 150 % display scaling the 4.1.5q dialogs are clipped: their OK/Cancel buttons lie outside the visible client area, so click them with `--click-button`, not `--at`.
- `C:\Windows\System32\notepad.exe` on Windows 11 is the packaged Notepad (a hardlink into `WindowsApps`): launched directly it exits with code 0 without a window (`launch` reports `the program exited during startup`). Use a classic Win32 target (`dxdiag.exe`, `charmap.exe`) for a screen-check smoke test on this machine.

Worked example (the oracle's module selection, evidence in `M:\projects\scratch\p7-screen-evidence`):

```
W='M:\projects\scratch\p7-screen-evidence\oracle-private' # Data\, plugins.txt, temp\, cache\, backup\, mygames\
cargo xtask gui launch "$XEDIT_ORACLE_DIR/xFOEdit.exe" \
--args -FO4 --args "-D:$W\Data\\" --args "-P:$W\plugins.txt" --args "-T:$W\temp\\" \
--args "-C:$W\cache\\" --args "-B:$W\backup\\" --args "-S:$W\\" \
--args "-M:$W\mygames\\" --args "-I:$W\mygames\game.ini" --args "-G:$W\mygames\Saves\\" \
--session M:/projects/scratch/p7-screen-evidence/fo4edit-session.json --desktop xedit-gui-fo4 --wait 40
cargo xtask gui windows M:/projects/scratch/p7-screen-evidence/fo4edit-session.json # TfrmMain + TfrmModuleSelect
cargo xtask gui click M:/projects/scratch/p7-screen-evidence/fo4edit-session.json --title "What's New" --at 820,600
cargo xtask gui shot M:/projects/scratch/p7-screen-evidence/fo4edit-session.json --title "Module Selection" --out module-selection.png
cargo xtask gui keys M:/projects/scratch/p7-screen-evidence/fo4edit-session.json --title "Module Selection" \
--control TLabeledEdit --keys "text:FO4" # the filter box shows FO4
cargo xtask gui close M:/projects/scratch/p7-screen-evidence/fo4edit-session.json
```

`--json` prints the same result as the text output as JSON on every command.

### Capturing the Rust GUI (`xedit-gui --screenshot`)

Our GUI paints through eframe/wgpu, and on the private desktop `gui shot` cannot witness it: the window is hidden and has no composited surface, so `PrintWindow` and the `BitBlt` fallback both return a black client area. The app captures itself instead, through egui's own screenshot path (`ViewportCommand::Screenshot`, which renders the frame to a capture texture and reads it back to the CPU even when the window is hidden):

```
cargo xtask gui launch D:\projects\xedit-target-p7-shot\debug\xedit-gui.exe \
--args --game --args sse --args --load --args Update.esm \
--args --screenshot --args 'M:\projects\scratch\p7-shot-evidence\sse-main.png' --args --exit \
--session M:/projects/scratch/p7-shot-evidence/sse-main-session.json --desktop xedit-shot-sse --wait 90
cargo xtask gui close M:/projects/scratch/p7-shot-evidence/sse-main-session.json
```

- `--screenshot <path>` writes one PNG of the window. It waits for the screen the other switches name: with `--load`, until the load finishes; with `--dialog <name>`, the dialog is opened as part of that same finish, so it is in the capture; with neither, the game or module selection dialog. It also captures the `Fatal:` screen of a failed load.
- `--screenshot-frames <n>` (default 2) is the number of painted frames to wait after the screen is ready, and `--screenshot-delay <ms>` a further wall-clock wait, for content that arrives on a worker thread.
- `--exit` closes the window once the PNG is written, so a test cannot hang; a screen that is not ready after 180 s closes without a PNG (the `XEDIT_GUI_LOG` line says why). The exit costs one `ViewportCommand::Close`; settings are saved on the close request as usual.
- The capture is the app's own pixels, so it proves what egui laid out, not what a Win32 compositor would show. Use `gui windows`/`gui keys`/`gui close` against the process as normal; only `gui shot` is replaced. `xtask gui shot` stays the right tool for the 4.1.5q Delphi binary and its GDI dialogs.
- A blank or black PNG means the app's frame was not painted before the read-back (or the GPU adapter refused the capture): check `XEDIT_GUI_LOG` for `screenshot: wrote <path>` and `screenshot: could not write ...`. The file only appears once the callback has run.
- On this machine the loaded screen opens the upstream developer message (step 6's `startup_dialogs`) on the first ready frame: the SSE settings file does not exist and FO4's has `[DeveloperMessage] Version=0`. The `--dialog <step-5 name>` runs suppress it (their dialog opens inside `finish_load`), and an unknown `--dialog main` opens nothing and leaves the bare shell; capture both states when the gate needs the shell without a modal. `sse-main-developer-message.png` and `sse-main.png` in the phase-7 evidence are the two. A step-6 `--dialog` name (`tip`, `script`, ...) is not capturable alone yet: the dialog-only path's `show_dialogs` consumes the pending name on a Loading pass, so `finish_load` sees none and `startup_dialogs` opens the developer message over the viewer; fix step 6's ordering before the gate uses those views.

Idle rule that the capture work exposed: eframe 0.37 calls `App::ui` only when a repaint is requested, so an idle window costs no CPU — but every `send_viewport_cmd` requests a repaint of its viewport. Sending the window title/icon per frame repainted the loaded window continuously at a full core on the private desktop; they are now sent once. When adding a viewport command, send it on change, not per pass, and schedule `request_repaint_after` only while an animation or a job is in flight. A hidden window runs `App::logic` only; `--screenshot` polls the load and its own state there, and asks for painted frames with `ViewportCommand::RequestPaintWhileHidden`.

## Inputs

- **Corpus:** vanilla master files from the local game installs, found through `XEDIT_FO4_DATA` and `XEDIT_SSE_DATA` (each is the game's `Data` directory). Never commit game files. Small synthetic plugins live in `tests/fixtures` and are committed.
Expand Down
Loading