Sisyphus

AI augmented focus tracker and game-launch speed bump

Available at: sisyphusfocus.site

Issue / Solution

The Issue: Digital distraction and impulsive game launches (like League of Legends) are friction-free, often triggered automatically by muscle memory when a user encounters a small mental barrier during work. Standard website blockers or lock-down tools are overly coercive, triggering a defensive reaction where the user simply uninstalls the blocker, rather than training their focus habits.

The Solution: I built Sisyphus, a lightweight Windows utility that acts as a deliberate "speed bump." Using Windows Image File Execution Options (IFEO) registry hooks, Sisyphus intercepts game launches before the process executes. Instead of locking down the PC, it displays a checklist dashboard. The user must check off their actual work items before the game opens, introducing a mindful delay that breaks the automatic loop of distraction.

Overview

Sisyphus is a Windows desktop app (now at v2.0) that puts a deliberate moment of friction between you and the things that derail focus. When you launch a blocked game or open a distracting website, a gate appears, and you can't proceed until you've checked off the work you said you'd do first. It was built for League of Legends, but it can block any .exe or website.

Beyond the launch gate, Sisyphus works as an AI-augmented focus tracker. It quietly logs where your screen-time actually goes, scores it against goals you set, drafts daily objectives and to-dos with AI assistance, surfaces AI-generated feedback on how each day went, and nudges healthier habits with smart stretch, sleep, and idle reminders. The name is the thesis: focus is a boulder you push up the hill every day, and the app's job is to make the right action the easy one and the distracting action require a conscious push.

Design Philosophy

Sisyphus v2.0 is built as three .NET 8 projects spanning ~50 source files, backed by 128 unit tests, and ships with 5 UI languages and 8 color themes. Four principles shaped every design decision underneath that.

No server, no account

Everything lives in local JSON under %AppData%\Sisyphus. The only network calls are an optional GitHub update check and the user's own AI provider — the API key never leaves the machine. Privacy by construction.

Pure core, thin shells

Sisyphus.Core holds UI-free, deterministic domain logic so it can be unit-tested and shared by both the WPF app and the launcher. The decision logic of the risky features (version compare, hash verify, streak math, IFEO name validation) is pure and tested.

Best-effort enforcement, never crash

Blocking, tracking, and updating are all "best-effort": a registry hiccup or I/O blip degrades gracefully (open the web page, skip a sample) rather than taking down the app. Stores swallow load errors and return empty rather than throwing into the UI.

Save-and-restart over live mutation

Theme and appearance changes persist to config and relaunch the app rather than trying to re-skin every open window live (which proved unreliable in WPF). A clean restart guarantees every window picks up the new palette, and config is already on disk, so nothing is lost.

Solution Architecture

The codebase splits into a pure domain core and two thin shells: the always-running dashboard and a tiny launcher that the blocked game's IFEO key redirects to.

Sisyphus.Core

Pure domain layer

No WPF, mostly testable, shared by the app and launcher. Config/activity/session/allow/notes stores, analytics categories, progress stats, release manifest + SHA-256 sums, the IFEO registry writer, startup manager, theme presets, and the cross-process lock.

Sisyphus.App

WPF dashboard & host

The composition root and every window (settings, onboarding, todos, analytics, notepad, gates) plus the always-running enforcement/analytics services: activity tracker, site/app blockers, foreground watcher, theme + accessibility managers, update + AI coach services.

Sisyphus.Launcher

The redirect target

The tiny process a blocked game's IFEO key points at. It parses the original command line, short-circuits on a bypass flag, shows the gate, and then launches (or doesn't) the real game.

Version is single-sourced in Directory.Build.props; the installer is built with Inno Setup via a PowerShell script.

The Blocking Gate — How It Actually Works

Sisyphus blocks in three layers, because no single mechanism covers every kind of program. Rather than running a CPU-heavy background watchdog, the primary layer registers directly under Image File Execution Options (IFEO) in the Windows Registry.

Layer 1 — Process Interception Flow (IFEO Registry Hook)

User triggers game.exe Windows OS Checks IFEO Registry Sisyphus.Launcher.exe WPF Checklist UI Real Game Launched SISYPHUS_BYPASS=1 Process Cancelled User returns to work Close / Bypass X Todos Done

Layer 1 — Image File Execution Options (IFEO) redirect

For a blocked game.exe, Sisyphus writes a Debugger value under the HKLM IFEO key for that exe name (requires admin, done via an elevated --set-ifeo relaunch of the app itself). Windows then launches the Sisyphus launcher instead of the game, passing along the original command line. The security-critical trick is that the launcher then re-launches the very exe that has an IFEO debugger pointing back at it. To avoid an infinite loop, it uses CreateProcess with DEBUG_ONLY_THIS_PROCESS (which bypasses the debuggee's IFEO key), then immediately detaches and clears kill-on-exit. The exe name is normalized to a bare *.exe filename before it can become a registry subkey (path-traversal stripped), validated by dedicated unit tests.

Layer 2 — AppBlocker (running-process kill)

IFEO can't catch packaged/UWP apps or processes that are already running, so a 1 Hz timer enumerates processes by name, kills blocked ones that aren't currently allowed, and raises the gate. After a gate is passed, the exe is written to the allow store with a 12-hour allowance so it isn't instantly re-killed.

Layer 3 — SiteBlocker (browser tab)

A second 1 Hz timer checks whether the foreground window is a known browser whose title contains a blocked brand token; if so, it sends Ctrl+W to close the tab and raises the website gate, with an 8-second per-(browser, pattern) cooldown so it doesn't fight the user.

The allow store is shared by two processes — the launcher writes the allowance, and the app's watcher prunes and reads it — so it is guarded by a cross-process mutex and atomic writes. That guarantees the watcher can't drop the allowance the launcher just wrote and kill the game the gate just launched.

Sisyphus planning and coding session outdoors
Sisyphus planning and coding session outdoors: Sisyphus running on a laptop during a vibe-coding session outside. Testing software load and process intercept triggers under high-glare outdoor focus conditions to verify display contrast and checklist gate responsiveness.

App Lifecycle & Orchestration

App.xaml.cs is the composition root. On startup it handles the elevated --set-ifeo/--remove-ifeo admin commands and exits if present; takes a per-session single-instance mutex (a second launch just surfaces the existing window); applies the saved color theme before any window loads; and registers a class-level Window.Loaded handler so every window gets accessibility settings applied automatically. It then wires the long-lived services — activity tracker, site/app blockers, foreground watcher, the global notepad hotkey, and timers for stretch reminders and a 1-minute tick (recurrence resets, bedtime, idle prompts) — before branching to either the first-run wizard or the dashboard.

The app runs with ShutdownMode=OnExplicitShutdown and disposes every service on exit. Settings changes are applied by persisting config and calling a restart that releases the single-instance mutex, relaunches via Environment.ProcessPath, and exits.

Data & Persistence

All state is JSON under %AppData%\Sisyphus, each file owned by a small repository class with a consistent recipe: a cached load, an atomic temp-file write (write to *.tmp, then File.Move), and a read-time Normalize pass that trims, defaults, clamps, and migrates old data.

Migrations are config-version aware: the v2.0 release stamps an onboarding version so the "everyone re-runs setup once" upgrade fires exactly one time, and adopts a new notepad default text size for users still on the old default.

Analytics & Goals

The activity tracker samples the foreground window on a timer (driven by the foreground watcher so it doesn't tight-poll), credits elapsed wall-time (capped at 1 hour) to the current app/title unless the user is idle, and persists on a 60-second cadence. A foreground detector resolves the window to a (process, title) pair — with extra effort for terminals, using UI Automation tab titles and a process-tree walk to label CLI tools.

Each app or site is classified into one of six categories, with per-process and per-site user overrides plus custom categories. The Analytics window renders day, week, month, year, and session ranges as bar and circle charts, a category breakdown, and most-used apps, alongside a goals-first section: each goal shows a progress bar and percentage computed from either the apps the user assigned to it or all time in the goal's category. The pure streak and progress math uses a 10-minute "active day" floor. A "Share" button renders a progress-card image, and an optional "AI grade" asks the coach for a letter grade. Sessions shorter than 15 minutes are filtered out of the Session view.

AI Coach & Smart Reminders

Sisyphus is more than a blocker — it doubles as a lightweight focus coach. The AI coach is a provider-agnostic single-turn chat client (OpenAI / Anthropic / Gemini). It builds a system prompt from the user's todos and goals plus a structured 7-day context JSON, and the model can return an upsert_goals JSON plan that is parsed, clamped, and applied back into config. The API key is sent only in provider headers — never logged, never in a request body. Three modes keep raw JSON out of the chat surface: Free chat, Coaching (prose only), and Auto-plan (JSON). The notepad can run a note through the same service to extract todos or get coaching, so the checklist that gates a game launch reflects real goals instead of busywork.

On top of focus, the app watches for healthy-habit cues and fires smart reminders to stretch, wind down for sleep, and step away after long idle or continuous-use stretches.

Theming & Accessibility

Eight presets (pure data), including three light themes. Applying a theme mutates the shared color of the seven themed brushes in place, so every consumer repaints, and the brushes carry alpha for the app's translucency. A hard-won lesson fixed in v2.0: brush references inside the theme's own styles and templates must use {DynamicResource}, not {StaticResource} — a static setter resolves at parse time (before the theme applies) and captures the original white brush, which was invisible on dark themes but unreadable white-on-light on the light themes.

Accessibility settings apply a text scale (a root layout transform), an optional text-color override, and a font family to every window via the class-level loaded handler. The compact dashboard overlay opts out of content scaling and uses its own timer-size control.

Self-Update & Onboarding

The update service checks GitHub's "latest release" endpoint (unauthenticated) and delegates all parsing and version logic to a pure, tested release-manifest type. On "Yes", the installer is streamed to temp and its SHA-256 is verified against the release's SHA256SUMS before it runs elevated and silent, after which the installer relaunches Sisyphus. If a release publishes sums, a matching hash is mandatory — a missing or mismatched hash aborts to the releases page rather than running an unverified binary elevated.

Onboarding is a 6-step wizard with a live theme preview; nothing touches the real app until Finish (which persists and restarts). A version gate re-runs it once for everyone upgrading to a major release, without data loss, while new installs always see it on first launch.

Engineering Patterns

What I Built

Design Choices

The app is intentionally transparent: config is plain JSON under %APPDATA%, blocks are removable from the UI, and there is no password or anti-tamper mechanism. The goal is friction and visibility, not coercive control.

Launch & Traction

Sisyphus reached 50+ users within the first week of its V1.0 release through sisyphusfocus.site. I kept an active line of communication with early users and shipped monthly updates driven by their real-time feedback, refining the launch-gate flow, activity analytics, and reminder behavior release over release.

Stack

.NET 8, WPF, Windows Registry, IFEO, Win32 interop, JSON persistence, foreground window tracking, cross-process locking, AI/LLM integration (OpenAI / Anthropic / Gemini), SHA-256-verified updates, Inno Setup, x64 Windows packaging.

(go back)