From 108fd8bf80f66ef072c20103af6f278027e4a475 Mon Sep 17 00:00:00 2001 From: 0xrsydn Date: Tue, 22 Sep 2026 15:13:45 +0700 Subject: [PATCH] docs(ui): document overlay setup and verification Describe the compact panel, module boundaries, build and installation steps, read-only limits, and manual acceptance checks. Record user-confirmed in-game readability. --- ui/JevOverlay/README.md | 198 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 ui/JevOverlay/README.md diff --git a/ui/JevOverlay/README.md b/ui/JevOverlay/README.md new file mode 100644 index 0000000..d982739 --- /dev/null +++ b/ui/JevOverlay/README.md @@ -0,0 +1,198 @@ +# Jev decision overlay + +A separate, read-only C# mod for Slay the Spire 2. It displays the bot's existing +`decisions.jsonl` file inside the game. It does not depend on STS2MCP at runtime. +The bot still uses STS2MCP to observe and control the game. + +## UI plan + +The first version uses the compact panel selected during planning: + +```text +┌ Jev decisions · read only ───────── [−] ┐ +│ Read-only log · no new rows for 2s │ +│ #42 · JEV · combat │ +│ Session: … │ +│ Proposed: play_card │ +│ Parameters: {"card_index": 0} │ +│ Proposal only; execution not recorded. │ +│ Reason: … │ +│ Recorded confidence: 0.81 · 720 ms │ +│ [Show questions and answers] │ +├ Recent decisions · select to inspect ──┤ +│ #42 JEV play_card │ +│ #41 CODE use_potion │ +│ #40 FALLBACK end_turn │ +│ [Follow latest] │ +└────────────────────────────────────────┘ +``` + +- The panel starts at the top right. F8 or the header button collapses it. +- Selecting a history row stops automatic selection of new decisions. +- `Follow latest` resumes automatic selection. +- The details button shows recorded Jev questions, answers, model, and latency. +- Text areas and history scroll independently. Evidence uses plain text, not markup. +- Source names remain visible. Blue identifies Jev; amber identifies fallback. +- Only the panel receives mouse input. There is no full-screen input blocker. +- The panel uses Godot's default font and controls. It needs no asset pack. + +This is a desktop mouse-and-keyboard first version. Dragging, position persistence, +controller navigation, filtering, and a remappable shortcut are not included. +The panel does not pause the game or bot. Collapse it if it covers a game control. + +## Module boundaries + +```text +ui/JevOverlay/ + Mod.cs mod startup, frame callback, background polling + Core/ + OverlayConfig.cs explicit absolute log path + LogTail.cs bounded, incremental JSONL reader + DecisionHistory.cs session/step joins and display text + UI/ + OverlayView.cs Godot controls and selection state + Tests/ + Program.cs temporary-file integration checks + JevOverlay.csproj game-facing assembly + JevOverlay.json game mod manifest +``` + +`Core/` has no Godot or game dependency. `UI/` has no file access or game API calls. +`Mod.cs` connects them. All controls are built-in Godot nodes, so the mod needs no +Godot editor project, custom node source generator, Harmony patch, or `.pck` file. + +The game mod belongs under `ui/`, not a web `frontend/` directory. Python policy, +the runner, and vendored code remain unchanged. + +## Build + +Use a .NET 9 SDK. No NuGet packages are needed; `NuGet.Config` clears package feeds. +The game assembly paths are explicit. Builds do not install or start anything. + +If `dotnet` is not on your PATH, a temporary Nix shell is sufficient: + +```sh +nix shell nixpkgs#dotnet-sdk_9 +``` + +A project flake and direnv configuration are not required for this version. + +Run these commands from the repository root. On macOS arm64: + +```sh +GAME_DATA="$HOME/Library/Application Support/Steam/steamapps/common/Slay the Spire 2/SlayTheSpire2.app/Contents/Resources/data_sts2_macos_arm64" +dotnet build ui/JevOverlay/JevOverlay.csproj -c Release \ + -p:STS2GameDataDir="$GAME_DATA" +``` + +On other platforms, set `STS2GameDataDir` to the installed game's directory +containing `sts2.dll` and `GodotSharp.dll`. Use assemblies from the target game build. + +Output: `ui/JevOverlay/bin/Release/net9.0/`. +Do not distribute game assemblies with the mod. + +## Offline checks + +The integration checks need .NET 9, but no game installation or connection: + +```sh +dotnet run --project ui/JevOverlay/Tests/JevOverlay.Tests.csproj -c Release +``` + +The checks pass temporary files through the actual reader, history, and text formatter. +They cover partial writes, UTF-8, malformed rows, session joins, action errors, +rotation, truncation, bounded history, large logs, missing files, and recovery. +They do not test Godot rendering or game input behavior. + +## Manual installation + +Do this only when you intend to load the mod. Close the game first. + +1. Build against the installed game assemblies. +2. Copy `JevOverlay.dll` and `JevOverlay.json` from the output directory into the game's `mods/` directory. +3. Copy `JevOverlay.config.example.json` there as `JevOverlay.conf`. +4. Set `log_path` to the absolute path of the bot's decision log. +5. Restart the game and enable mods if prompted. + +On macOS, the mod directory is: + +```text +SlayTheSpire2.app/Contents/MacOS/mods/ +``` + +Example configuration in `JevOverlay.conf` (JSON content, not a mod manifest): + +```json +{ + "log_path": "/absolute/path/to/sts2-bot/capture/decisions.jsonl" +} +``` + +Do not use `~` or environment variables in this value. For Windows JSON paths, +use forward slashes or escape backslashes. Custom `run.py --capture-dir` locations +need a matching `log_path`. + +The reader waits if the file does not exist. Missing or invalid configuration +appears in the panel. Configuration changes require a full game restart. +The mod never creates or changes the configured log file. + +To remove the mod, close the game and remove these three installed files. +No saves, capture files, or existing mod files need to change. + +## Log meaning and limits + +The viewer consumes the current `run.py` trace format: + +| Event | Display behavior | +| --- | --- | +| `decide` | Add a proposed action, identified by session and step | +| `action_rejected` | Mark that action request as rejected | +| `action_error` | Show the request error; completion remains unknown | +| `session_end` | Show the session's stop reason, not action completion | +| Model/policy failures, `no_decision` | Show a reader notice | +| Other events | Ignore without treating them as malformed | + +The log does not record successful action results or a per-decision dry-run flag. +The viewer cannot distinguish an unconfirmed live proposal from a dry-run proposal. +It never marks a proposal as executed because the next decision arrived. +A transport error does not prove that the game failed to execute a request. + +The policy reason is recorded text, not private model reasoning. A `jev` response +can accompany a fallback; the `source` field identifies the actual policy path. +Recorded answer gates can differ from policy-specific final gates. Confidence is +not proof of correctness. No Jev response means only that none was recorded. + +The status reports file activity, not bot or game liveness. Existing history can +be displayed when no bot is running. Sessions remain distinct, including when +multiple runners append to the file. The latest appended decision is selected +when `Follow latest` is enabled; there is no session filter in this version. + +The reader polls every 500 ms on one background worker. It reads at most 256 KiB +per poll and starts within the last 256 KiB of an existing log. It keeps at most +100 decisions and at most 128 KiB per incomplete line. Oversized or malformed +lines are skipped. Display text is limited to 16,000 characters per text area. +A displayed decision can leave history while it is selected. + +Truncation and replacement are detected by file length, a prefix, and a checkpoint +near the last read offset. A replacement with identical sampled bytes and no +length decrease cannot always be distinguished from an append. This viewer is +not a durable audit reader. Concurrent writers must still produce complete JSONL +rows; the viewer cannot repair interleaved writes. + +## In-game acceptance check — requires explicit approval + +No live game action or model call is needed to validate the panel itself. Point +`log_path` at a disposable JSONL file and use synthetic records: + +1. Start the game with the mod installed and check the initial panel. +2. Append a `decide` record with a session, step, and action. +3. Confirm the panel updates and does not mark the proposal as executed. +4. Expand the evidence area and select an older decision. +5. Append another record and confirm the older selection remains selected. +6. Test F8, collapse, scrolling, resizing, and clicks outside the panel. +7. Switch game scenes and confirm there is exactly one panel. +8. Remove or truncate the disposable file and confirm reader recovery. + +The user confirmed that the installed panel appears and is readable in-game. +Input routing, scene persistence, and the remaining acceptance steps are not yet +verified. Building against game assemblies is not a substitute for these checks.