Commit Graph

16 Commits

Author SHA1 Message Date
jessey bf3ac4af1a feat(m9): systemd --user trigger modes + game-launch watcher — 0.23.0
D6 trigger modes, no root:
- core/service.py: write/enable `systemd --user` units; apply_mode(manual/
  always-on/game-launch) reconciles the recorder + watcher services; status().
- core/watcher.py + `rigdoctor watch`: poll Steam RunningAppID, auto-bracket a
  focused capture (D12 zero-config fallback; wrapper stays primary).
- CLI `rigdoctor service status|mode`; config `trigger_mode`.
- GUI Settings: "Recording trigger" dropdown (Apply runs apply_mode off-thread).
- Tests for unit generation, mode reconciliation, watcher transitions/parse.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:55:36 +02:00
jessey 67665974dc feat(m6): PowerMizer + Wine/Steam versions + non-Steam launchers — 0.22.0
M6 leftovers (the watcher defers to M9's trigger-mode work):
- gameenv: check_gpu_powermizer (NVIDIA, X; degrades when the gpu target won't
  resolve), check_wine (wine --version), check_steam_client (dpkg package version);
  steam.client_version() helper.
- core/launchers.py: detect Lutris (read-only SQLite pga.db) and Heroic (Epic
  legendary + GOG JSON) installed games; Game gained a `launcher` field.
- Games page + `rigdoctor games` list non-Steam games alongside Steam, tagged by
  launcher; Run Diagnostic works on them (auto-launch stays Steam-only).
- Tests for launchers (synthetic Lutris db + Heroic json).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:46:42 +02:00
jessey 6fca2c9aba feat: live monitor TUI (M2) — 0.21.0
Upgrade `rigdoctor monitor` from a basic redraw to a stdlib curses dashboard
(tui.py): current / session-min / session-max per sensor, grouped by subsystem,
with temperature & utilization color bands (GPU-lost flagged red). q quits,
r resets min/max. Plain full-screen redraw fallback on a non-TTY (--plain forces
it). Pure track()/band() helpers are unit-tested; curses path verified in a pty.

Completes the Monitoring bundle (M2 + M8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:37:57 +02:00
jessey 75a4da7af3 feat(gui): system-tray applet (M11) + GUI smoke tests — 0.19.0
QSystemTrayIcon applet (gui/tray.py, D13): menu with live CPU/GPU temp + memory
used/total, a status line, a Run Diagnostic submenu per detected game, plus Open
dashboard / Start-Stop recording / Snapshot-copy / Quit. Reuses the dashboard's
sample stream; drives existing MainWindow flows.

- MainWindow creates the tray when one is available; closing the window hides to
  tray (Quit exits); setQuitOnLastWindowClosed(False) so dialogs don't quit it.
- app: `--tray` starts hidden for autostart.
- tests/test_gui_smoke.py: construct MainWindow headless + exercise the tray, so
  a startup crash (like the 0.18.0 import bug) fails the build. Skips if no PySide6.
- docs: M10/M11 marked done in MODULES/ROADMAP.

Completes the Desktop UI bundle (M10 + M11).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 09:21:37 +02:00
jessey 03b2dd8363 feat: D12 Steam-launch wrapper for auto crash-capture + doc status fixes — 0.16.0
D12 "build first" wrapper: `rigdoctor wrap %command%` (Steam launch option /
Lutris/Heroic wrapper field) auto-brackets a focused diagnostic around a game —
start a game-tagged capture on launch, clean stop on exit; a hard freeze leaves
it unterminated → flagged as a crash next launch.

- core/wrap.py: game name from SteamAppId, PATH-proof launch_option(), run()
  that doesn't disturb an existing capture and returns the game's exit code.
- diagnostic.start() preserves an unanalyzed crash to diagnostic-crash.jsonl
  before clearing, so auto-relaunch can't wipe an unseen crash; pending_crash/
  analyze_crash check the archive first.
- GUI: "Auto-capture…" helper dialog (copyable launch-option string).
- Tests for wrap (name resolution, exit-code passthrough, no-double-start).
- docs: fix stale MODULES.md status column (M1/M3/M4/M5/M8/M10/M13 → done),
  update ROADMAP/MODULES for the wrapper + crash detection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:59:54 +02:00
jessey 934b489fec feat(gui): Run Diagnostic flow on the Games page — 0.12.0
Brings the guided diagnostic (0.11.0 core/CLI) into the GUI:
- Each game row gets a "Run Diagnostic" button → starts a focused, game-tagged
  capture and shows a recording banner (live sample count + GPU-lost indicator)
  with Finish & analyze / Discard.
- Finishing runs core.diagnostic.finish() off the UI thread and opens a results
  dialog (gui/diagnostic_dialog.py): window-scoped capture summary + findings
  cards (reusing render_summary + finding_card).
- Banner restores on showEvent if a capture is still running (navigate away/back).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:32:04 +02:00
jessey 5682878f22 feat: guided diagnostic session (CLI) — pick a game, capture, analyze — 0.11.0
The seed use case end to end, orchestrating M3 + M4 (ARCHITECTURE §7.1).

- core/diagnostic.py: start(game) runs a focused, game-tagged capture into a
  dedicated diagnostic log (window-scoped report, separate from the always-on
  crash log); finish() stops it and combines the capture summary (M3) with the
  health findings (M4). Game recorded as a log event so it survives crash+reboot.
- CLI: rigdoctor diagnose start --game/--appid | status | finish.
- recorder/record run gained an optional --game tag; reccontrol passes it through.
- Tests for game recovery + the finish() combination.

GUI/tray "Run Diagnostic" button and auto start/stop (D12 wrapper) come next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:27:53 +02:00
jessey 9c30c9824e feat(m6): one-click install + apply controls on Environment page — 0.10.0
Make the environment report actionable, not just advisory.

Install (reuses M9 installer):
- Add GameMode, MangoHud, cpupower to the component catalog (so they also show
  on the Setup page); catalog.by_id() lookup.
- "tool not installed" findings (GameMode/MangoHud) get an Install button.

Apply runtime-reversible tunables (D22, realizing the D9 consent-gated milestone):
- core/fixes.py: dropdown of live options + Apply for CPU governor, NVIDIA
  persistence, PCIe ASPM policy, vm.swappiness, THP. One pkexec command each,
  no reboot, reverts on reboot; chosen value validated against live options;
  writes go to sysfs/procfs/nvidia-smi, never GRUB. GRUB/mitigations stay
  suggestion-only.
- Finding gained optional action (install) + fix (apply) ids; shared
  finding_card renders the matching control; Environment page wires both and
  re-checks after a change.

Tests for fixes (parse, command builders, value validation, gameenv wiring).
Docs: D22 added (amends D9); SPEC/MODULES/ROADMAP updated. 0.9.0 -> 0.10.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:05:03 +02:00
jessey 29f4a45df8 feat: gaming environment checks engine (M6) + notification icon — 0.9.0
The evaluate-and-suggest half of M6: a read-only findings report (D9) over
system settings that affect gaming stability/performance, each with the exact
fix command.

- core/gameenv.py: PCIe ASPM, NVIDIA persistence mode, CPU governor (the three
  seed-case contributors to GPU bus-drop / Xid 79), GameMode, MangoHud,
  vm.swappiness, shader disk cache, THP, CPU mitigations, Proton versions.
  Pure evaluate_* helpers split from IO for testing; reuses the M4 Finding model.
- steam.proton_versions(): surfaces installed Proton builds for the report.
- CLI: rigdoctor gameenv (text / --json); render_health() gained a title arg.
- GUI: new Environment page; extracted a shared finding_card widget and switched
  the Health page to it.
- Tests for the pure evaluators + aggregate.

Also fix: desktop notifications now use the RigDoctor icon (installed theme copy
-> bundled asset -> stock fallback) instead of a generic stock icon, matching
the app/dock icon.

Docs (MODULES/ROADMAP) updated; version 0.8.0 -> 0.9.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:53:06 +02:00
jessey 0642eb4712 feat: Steam game & library detection (M6) — 0.8.0
The first slice of M6 (gaming-environment checks): detect a user's Steam
libraries and the games installed in each — also the D12 "pick a game"
foundation.

- core/steam.py: multi-install/library discovery (libraryfolders.vdf, symlink
  dedupe, native/Flatpak/Snap), appmanifest_*.acf scan with runtime/Proton/
  redist filtering, scan cache + new-game diff. Stdlib only. VDF keys read
  case-insensitively (e.g. lastupdated vs SizeOnDisk).
- Libraries are opt-in (config steam_libraries); the flat TOML writer now
  emits list/array values.
- GUI Games page: library checkboxes with per-library counts, game list,
  background rescan on every launch, NEW badge + sidebar count for games
  installed since the last scan (acknowledged when viewed).
- CLI: rigdoctor games / games libraries [--enable|--disable|--all|--json]
  (headless-complete, D17).
- Tests for VDF parse, scan, tool filter, cache diff, config list round-trip.
- Docs (MODULES/ROADMAP) updated; version 0.7.3 -> 0.8.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:43:31 +02:00
jessey 2f6cab72c4 feat: shared PTY terminal (M12 Tier 3) + readable form controls
release / release (push) Successful in 14s
- feat(share): host-consented interactive terminal over the relay. The host shares
  a real PTY shell (core/pty_session.py); the guest renders it with pyte and sends
  keystrokes (gui/terminal_widget.py) — vim/top/tab-completion/Ctrl-C work. Runs as
  the host's user (never root). The host reads along live and can type too, e.g. a
  sudo password, which stays local and is never sent to the guest. Off by default.
  Guest also pulls inventory on join (req_full).
- fix(gui): style all form controls (QLineEdit/QPlainTextEdit/spin boxes/combo/
  terminals) dark-on-light-text — Fusion defaulted them to unreadable light-on-light.
- replaces the command/response shell with the full PTY; adds pyte to the gui extra.

Verified end-to-end against the deployed relay (guest keystroke ran on host PTY).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:16:22 +02:00
jessey 67d4c1cb99 feat: session sharing over the relay (M12) — Share tab
release / release (push) Successful in 14s
Add a Share tab that hosts or joins a read-only live session through the
rigdoctor-relay over WebSocket (QtWebSockets), gated by the Gitea access token.

- gui/share_page.py: Start shared session (host: get a code, stream snapshot +
  health + inventory) and Enter share code (guest: view a host's data read-only)
- core/share.py: host_full_frame / host_snapshot_frame + guest_html renderer
- config: relay_url (default wss://rigdoctor.jesseyvanofferen.com)
- setup: token now powers updates AND sharing — hint asks for read:user +
  read:repository scopes (relay validates the account via Gitea)
- main_window: Share nav tab + socket cleanup on close
- tests for the relay frame builders and guest HTML

Verified end-to-end against the deployed relay (host code -> guest frame).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:03:17 +02:00
jessey 46ba53631a Release 0.0.7: user-local installer + self-update apply
release / release (push) Successful in 22s
- install.sh: no-root user-local install (private venv + ~/.local/bin launchers
  + desktop entry); --ref <tag> to install a specific release, --uninstall to
  remove; auto-installs the python3-venv prerequisite with consent
- packaging/make-run.sh: build a self-extracting .run installer (makeself)
  bundling the wheel + install.sh; release workflow builds and attaches it
- M13 self-update apply: `rigdoctor update` runs an authenticated pip upgrade
  (rigdoctor[gui] @ git+https://oauth2:<token>@...@<tag>), token scrubbed; GUI
  sidebar "Update to v…" button applies it and prompts to restart
- version 0.0.7, CHANGELOG, docs (M9/M13, ROADMAP, README install section)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:12:53 +02:00
jessey 2e6a981120 Release 0.0.5: health report (M4), installer (M9), update check (M13)
release / release (push) Successful in 13s
M4 — health report (the 0.0.4 CHANGELOG entry, folded into this release):
- core/health.py: scan journalctl (Xid/panic/OOM/MCE/AER/thermal), SMART,
  NVIDIA driver mismatch, journald persistence, live temps -> findings
- CLI `rigdoctor report` (text/JSON); GUI Health tab; scanner tests

M9 — installer (first cut):
- core/{catalog,sysenv,installer}.py; `rigdoctor install [--check] [-y]`
- GUI Setup tab: detect distro/GPU, show optional components, one-click
  install of missing apt packages via pkexec/sudo

M13 — update check (check half):
- core/updates.py; sidebar shows up-to-date / "Update to v…" / unavailable

Plus tests, version bump to 0.0.5, CHANGELOG, and doc status updates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:36:11 +02:00
jessey ce5f830393 Release 0.0.2: M3 logger (CLI + GUI), GUI-first, CI release workflow
release / release (push) Successful in 2m13s
Crash-capture logger (M3):
- crash-safe JSONL (fsync per sample), size-based rotation, GPU-lost/recovered
  markers, atomic status file
- CLI: record run/start/stop/status/report (run = systemd-ready entrypoint)
- shared core.reccontrol so CLI + GUI drive the same recorder
- crashlog tests (writer, rotation, reader, summary, recorder)

GUI:
- Recording/Logs page: start/stop/interval controls, live status, post-crash report
- shared render helpers (format_raw/headline, render_summary)

Docs/decisions:
- GUI-first (D17); CLI keeps full parity
- D8 revised: user-local self-updating install primary, .deb optional
- planned: M12 session sharing (D16), M13 no-root auto-update from public repo (D18)
- versioning + CHANGELOG convention (D19)

Infra:
- .gitea/workflows/release.yml: build wheel+sdist and publish a Gitea release
  v<version> on push to main
- align version to the 0.0.x release line; bump to 0.0.2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:16:41 +02:00
jessey 305b6c4497 Initial commit: docs, decisions, and M1 sensor core
Planning docs (SPEC, ARCHITECTURE, MODULES, ROADMAP, DECISIONS) with
decisions D1-D15 settled: RigDoctor name, Python 3 + Qt/PySide6 stack
(core/CLI/daemon stdlib-only), Ubuntu + NVIDIA first, .deb packaging,
read-only + suggestions, GUI + tray modules, stress module dropped.

First code: the M1 sensor core (stdlib-only) and a CLI.
- core engine: Reading/Sample model, Sampler, hwmon reader
- self-probing sources (NVIDIA first): nvidia-smi GPU, coretemp/k10temp
  CPU, /proc/meminfo + DDR5 SPD memory, NVMe storage
- CLI: snapshot (text/JSON), monitor, sources; record/report stubbed
- stdlib unittest smoke tests

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:40:21 +02:00