sts2-bot/ui/JevOverlay/README.md
0xrsydn 108fd8bf80 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.
2026-09-23 13:40:02 +07:00

198 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.