What it does
Run Ledger wraps any command:
runledger -- python train_lora.py
It records when the job started and ended, the exit code, and a tail of stdout and stderr, and it samples the GPU every few seconds — VRAM, power, temperature, utilization — from nvidia-smi. A local page, with no account and no internet, lists the runs and charts each one. When the job finishes or crashes, it can POST once to a generic webhook or to a WeCom, DingTalk, or Feishu bot.
If nvidia-smi is missing, a demo sampler still draws a curve and the page says the curve is simulated. The exit code and the log tail stay real. runledger demo can also drop labeled example runs so the page is not empty in a recording. Those examples are badged as simulated. They are not measurements.
The wrapper returns the child's exit code. A webhook that fails does not change that.
Who it's for
A solo developer with one RTX 4090D on a Windows PC, running ComfyUI, LoRA training, or a local LLM. They start a job, leave the desk, and come back to a dead terminal whose scrollback is gone. They do not want a cloud experiment tracker, a login, or a notification service that is awkward to reach from mainland China. They already have a group-bot URL.
How it was built
Empty folder. The Devpost Learn Skill Pack (npx skills add challengepost/learn-ai-basics --all -y) ran before application code: scope, then a product requirements doc, then a technical spec, all in devpost/. The spec locked a proof of concept with no runtime dependencies: a Python CLI, SQLite, the standard-library HTTP server, and one offline HTML page.
The build followed that boundary. Wrap a command and read it back. Show it on the page. Ping a bot in four payload shapes. Add a labeled demo path so the page can be recorded without a GPU. Tests cover the exit code, the log tail, the nvidia-smi parser, the webhook bodies, and the local API.
What I learned
Planning the done line before code changed what got built. "A short command still shows up on the page, with at least one sample" is checkable. "It monitors the GPU" is not. That sentence is why the wrapper samples at the start and again when the process exits, and why tests/test_runner.py asserts a tail and a sample instead of a happy-path story.
The other lesson was honesty in the demo. A laptop with no NVIDIA driver cannot show a real 4090D curve. Faking one unlabeled would make the video a lie. Labeling the demo sampler, and badging runledger demo rows as simulated examples, kept the kernel — a real exit code and a real log — intact on hardware the judges might not have.
Log in or sign up for Devpost to join the conversation.