Skip to main content

test-plugin

End-to-end test this plugin itself — run the whole Auto-Triage loop (dispatcher, unattended sessions, blockers over Remote Control, db skills) against a local copy of the games db seeded with synthetic reports, flags, spam, deletion requests and gift/demo offers, while the VMs, snapshots, Steam account and UTM lease are the real ones; queue dispatched sessions that exercise any other skill (repro, provisioning, kernel debugging, host builds, validation, handoffs); set up and undo the scenarios that need a person (Steam signed out, an expired GitHub token, a force-push); judge each run against its pass criteria and turn every gap it shows into a general fix. Use after changing a skill, the harness or the dispatcher, or when asked to "test the plugin", "run the e2e tests", "validate the skills end to end" or "seed a test report".

Source facts

Repository
utmapp/utmapp-bot
Last source activity
November 7, 2026 at 22:57
Detected SKILL.md language
English
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
test-plugin
description
End-to-end test this plugin itself — run the whole Auto-Triage loop (dispatcher, unattended sessions, blockers over Remote Control, db skills) against a local copy of the games db seeded with synthetic reports, flags, spam, deletion requests and gift/demo offers, while the VMs, snapshots, Steam account and UTM lease are the real ones; queue dispatched sessions that exercise any other skill (repro, provisioning, kernel debugging, host builds, validation, handoffs); set up and undo the scenarios that need a person (Steam signed out, an expired GitHub token, a force-push); judge each run against its pass criteria and turn every gap it shows into a general fix. Use after changing a skill, the harness or the dispatcher, or when asked to "test the plugin", "run the e2e tests", "validate the skills end to end" or "seed a test report".
argument-hint
[scenario | skill to exercise | status]
# Test the plugin end to end Request: `$ARGUMENTS` Unit-level checks (`py_compile`, `bin/utmbot help`, `bin/gamesdb help`, `osacompile -o /dev/null scripts/provision/utm.applescript`, `scripts/check-portable.sh`) catch typos. They cannot show whether a skill leads a session to the right result. For that, a real session has to run the skill the way production does: started by the dispatcher, unattended, on the real VM. This skill sets that up without touching the production site. Run it from a checkout of this repository: the scripts below live in it (`R=<checkout>`; the commands assume `cd $R`). ## What is local and what is real - **Local:** the games db. `scripts/gamesdb/e2e-local.sh` runs the site from a [utm-games-db](https://github.com/utmapp/utm-games-db) checkout on `127.0.0.1:18080` (internal API on `:18081`), with its data in `~/.utmapp-bot/gamesdb-local/`, and a second dispatcher that polls it every 2 minutes, with its own state in `~/.utmapp-bot/gamesdb-e2e/`. Nothing it does reaches the production site. IGDB credentials for the local site (so new games get store links, as in production) go in `~/.utmapp-bot/gamesdb-local/.env`. - **Real:** the test and dev VMs, their snapshots, the UTM lease, the Steam account (licenses claimed and gifts accepted stay on it), CrossOver, and GitHub (fix branches go to the bot's fork). Treat them as production: a test that claims a license, pushes a branch or deletes a snapshot did so for good. - The e2e dispatcher and the production one share the UTM lease, so they never drive a VM at the same time. Pause production work for a long test run with `bin/gamesdb dispatch pause` (and `resume` after), or let them queue behind each other. Never point `e2e-seed.py` or test sessions at the production server. Seeds write to the local SQLite file directly, and it refuses any other database. ## Set up (once per session at the Mac) ```sh scripts/gamesdb/e2e-local.sh start <utm-games-db checkout> # local site + e2e dispatcher; skips what is up scripts/gamesdb/e2e-scenario.sh status # dispatcher, jobs, lease, VM roles, last log lines scripts/gamesdb/e2e-local.sh log 60 # the e2e dispatcher's log ``` Sessions drive production's UTM, `/Applications/UTM.app`; set `UTM_APP` before `start` to test another (a dev build), and `e2e-local.sh restart` to change it later. A dev build may lack what production has (D3DMetal needs the x86_64 render server). It needs a current UTM (`docs/utm-host.md`). The e2e dispatcher runs as production's does: a launchd job (`app.getutm.utmapp-bot.e2e-dispatch`) under the agent launcher, so macOS charges Local Network, UTM's container and Automation to "UTM Bot Agent", not to your terminal. Run `db-setup` first (it builds the launcher and asks for its permissions). A dispatcher started from a terminal would pass tests that fail in production. `e2e-scenario.sh status` is the one command that shows where every test stands. Reports and Auto-Triage comments show on http://127.0.0.1:18080. `bin/gamesdb local from-backup --tar <backup>` loads real reports from a backup into the local database (no secrets are copied) when synthetic ones are not enough. Pass `GAMESDB_URL=http://127.0.0.1:18081 GAMESDB_STATE=~/.utmapp-bot/gamesdb-e2e` to any `bin/gamesdb` command to aim it at the local instance. **The result check.** A triage that finishes records its comment with `gamesdb final`; the dispatcher runs `/utmapp-bot:db-triage-check` and posts it, or resumes the triage with the findings. To test the check on a real finished triage without redoing it: copy the report into the local db (`e2e-seed.py report` with its text, its earlier bot comments as `author_kind='bot'` rows), fork the triage session (`claude -p "<you are a test copy on local report N>" --resume <sid> --fork-session`), run its `gamesdb final` in its environment (`GAMESDB_MODE=auto GAMESDB_DISPATCH_JOB=triage:N CLAUDE_CODE_SESSION_ID=<fork>`), and give a fresh `GAMESDB_STATE` the triage job (status running, a dead pid, a log holding the fork's result). The dispatcher then checks it and, on a fail, resumes the fork with the findings. Note: `e2e-seed.py` defaults to `gamesdb-local/data/`; point `E2E_DB` at the database the local server actually serves (`gamesdb-local/test-data/gamesdb.sqlite` when it was started with `--data …/test-data`). ## Two ways to start a test 1. **Seed content and let the dispatcher find it.** This is the production path end to end: work feed, job planning, the session, its comment and status on the site. `scripts/gamesdb/e2e-seed.py` writes synthetic content (`--help` lists everything): ```sh scripts/gamesdb/e2e-seed.py games forspoken # IGDB ids the local db knows scripts/gamesdb/e2e-seed.py report --game <igdb id> [--rating R] [--issue T] \ [--backend dxmt|d3dmetal|unknown] [--utm 5.0.8] --desc "…" [--repro "…"] [--attach FILE …] scripts/gamesdb/e2e-seed.py reply <report> "…" # the reporter answers a clarification scripts/gamesdb/e2e-seed.py flag comment <id> --times 3 # moderation work scripts/gamesdb/e2e-seed.py spam comment "…" scripts/gamesdb/e2e-seed.py deletion <report> --reason "…" scripts/gamesdb/e2e-seed.py request gift|demo --game <igdb id> scripts/gamesdb/e2e-seed.py status <report> clear # re-queue a report ``` 2. **Queue a session with a prompt**, for skills the dispatcher never starts by itself (`repro-gpu-issue`, the provisioning skills, `kernel-debug`, `build-utm`, `validate-stack`, `investigation-handoff`, …): ```sh scripts/gamesdb/e2e-scenario.sh queue e2e-<name> "<prompt>" [triage|moderate|…] ``` The kind decides whether the job needs UTM (`triage` does). Write the prompt as a maintainer would ask, naming the skill and what to report, and state the pass criteria for yourself before it runs, not after. Long jobs run for hours. Check back with `e2e-scenario.sh status` instead of watching. ## Scenarios Seeded or queued by Claude, no person needed: | Scenario | Set-up | Passes when | |---|---|---| | Already fixed | a report of a bug that reproduces on the release stack and is fixed in the dev UTM or dev driver | `wont_fix`, the comment names the build that fixes it | | Not reproduced | a report of a symptom the released driver no longer has (an old guest tools version) | `needs_clarification`, asking for exactly what is missing (here: update the guest tools) | | A real bug to the end | a report standing in for a known open bug, worded as a player would (`e2e-scenario.sh fix-proposed setup` seeds one) | reproduced at the reporter's settings, CrossOver compared, the layer named only after attribute-to-layer, validate-stack and perf-regression ran, a branch `triage/report-<id>-…` on the bot's fork per repository and no pull request, `fix_proposed` with the links | | Clarification answered | `e2e-seed.py reply` on a report in `needs_clarification` | the **same** session resumes, uses the answer, comments | | No copy of the game | a report for a game the account does not own, no demo | a synthetic repro if the report pins down an API path, else db-summarize notes; either way the reporter gets a comment and the status goes back | | Attachments with personal data | the above with attachments full of names, paths, IPs, MACs, serials, Steam IDs, e-mail addresses | nothing personal in the comment, nothing says there were attachments. Made-up logs are recognised as made-up and rightly not summarized: test the summary itself with a real UTM debug log of a game, with the account and VM names changed and a configuration that matches the report | | Moderation | flags on a spam comment and on a legitimate report, spam and ham in the spam log, a redaction request, a vague deletion request | spam removed, griefing flags cleared, Akismet trained, the redaction drafted (or hidden when the server cannot edit), everything else left for a person and listed | | Demo offer | `e2e-seed.py request demo` for a free demo | license claimed, `steam-app-<appid>` snapshot made, game owned on the site, request closed | | Gift offer with no gift | `e2e-seed.py request gift` without sending one | no gift accepted, nothing marked owned, the request stays open | | `in_progress` safety net | a queued session told to end with its report `in_progress` | resumed with the safety-net prompt twice, then marked failed | | Scheduled return | a queued session told to run `session later --hours 0.05` | the dispatcher brings the same session back on time | | Another skill | `queue` with a prompt for that skill | what the skill's own Report section promises, with evidence | Scenarios that need a person (each sends exactly one push notification): | Scenario | Set-up | The person | Passes when | |---|---|---|---| | Steam signed out | `e2e-scenario.sh steam-signout setup` (keeps signed-in copies as `*-e2e-good` and seeds a report) | signs in in the VM window, replies "done" | one push; other Steam work held (`--scope steam`); afterwards the session deletes the stale game snapshot, reinstalls it from the new baseline and triages. Then `steam-signout cleanup` (or `restore` if it went wrong) | | GitHub token expired | `e2e-scenario.sh github-expired setup` | runs `github-expired fix`, replies "fixed" | the blocker has `--scope github`; the session ends with `GITHUB OK as <bot>`. Then `github-expired restore` | | Force-push | `e2e-scenario.sh force-push setup` | answers yes or no | the session asks before pushing and does what it was told (`force-push check`) | | Interactive vs auto | while a VM job runs, `/utmapp-bot:db-triage <id>` in a terminal | watches | the session names the job holding UTM and since when, and waits in 10-minute steps, saying so each time | | Network loss | turn Wi-Fi off for 3 minutes during a triage | nothing | `bin/gamesdb tunnel status` goes to waiting, then connected; the session carries on, or is resumed about 15 minutes later as an outage; no push | | A real gift | gift a game to the triage account from another account, then `e2e-seed.py request gift --game <id>` | sends the gift | accepted, license confirmed, owned on the site, request closed | | macOS permission dialog | `tccutil reset ScreenCapture <terminal's bundle id>`, then queue a session that samples frames (`utmbot frames`) | allows the dialog, replies | one push; the work resumes | Some things only real traffic can test: notes posted from a reporter's own debug log, and the first dev driver that fixes something in the released one. Note them as open rather than faking them. ## Judge the run, then fix in general terms For each run read the session's summary (`e2e-local.sh log`), the comment and status on the local site, the run directories it names, and what it left in `~/.utmapp-bot/games/`, `~/.utmapp-bot/notes/` and `~/.utmapp-bot/agent-tools/`. A pass needs the evidence the scenario names, not the session's word for it. When a run shows a gap, fix the plugin, not the test: - Fix the harness, the dispatcher or the skill so that any session in that situation does the right thing. Never add a step, an exception or a wording that only matters for the test (a seeded report id, the local site, a scenario's name): skills and docs describe production behaviour only, and this skill is the one place that knows about testing. - Re-run the scenario after the fix, and only then count it as passed. - Commit the fix with what the run showed in the message. Test results do not go into the repository: keep them in `~/.utmapp-bot/gamesdb-e2e/results.md` (one row per run: date, scenario, result, what was fixed). ## Clean up ```sh scripts/gamesdb/e2e-seed.py cleanup # every seeded report, comment, flag, request and user scripts/gamesdb/e2e-local.sh stop # dispatcher and local site (running sessions go on) ``` Delete VMs, snapshots, bottles and fix branches a test created only when they were made for the test (`e2e-*` names); a snapshot such as `steam-app-<appid>` from a demo test is a normal cache and can stay. ## Report Each scenario run: what it checked, pass or fail with the evidence, the gaps found and how each was fixed (commit), and what still needs a person or real traffic.
View on GitHub