Rework
README.md
<p align="center">
<img src="assets/logo.png" alt="Rework logo" width="140">
</p>
<h1 align="center">Rework</h1>
<p align="center">
<strong>Verified, evidence-based project-completion tracking for AI agents — over MCP.</strong>
</p>
<p align="center">
<img alt="Version" src="https://img.shields.io/badge/version-0.2.0-orange">
<img alt="Python" src="https://img.shields.io/badge/python-3.11%2B-blue">
<img alt="Tests" src="https://img.shields.io/badge/tests-130%20passing-brightgreen">
<img alt="MCP tools" src="https://img.shields.io/badge/MCP%20tools-5-informational">
<img alt="Platform" src="https://img.shields.io/badge/platform-Windows-lightgrey">
</p>
<p align="center">
<a href="#why">Why</a> ·
<a href="#the-5-tools">Tools</a> ·
<a href="#the-core-loop">Core loop</a> ·
<a href="#desktop-dashboard">Dashboard</a> ·
<a href="#setup">Setup</a> ·
<a href="#seen-in-action">Demo</a>
</p>
---
## Why
When an AI agent works through a task list, "I finished X" is unverifiable by default. The agent could have written a tautological test (`assert True`), a test that never actually ran, or a test that passes regardless of whether the underlying logic is correct. A self-reported checkbox and a real fix look identical from the outside.
**Rework closes that gap.** It's an MCP server — plain enough that Claude Code, Claude Desktop, or any other MCP client can drive it — that forces every completion claim through one of two paths:
- **Automated proof**: a real FAIL_TO_PASS/PASS_TO_PASS test cycle, plus mandatory mutation-checking that deliberately breaks the target code to confirm the test would actually catch a real bug.
- **An explicit, recorded judgment call** — human or `claude_review` — for the cases automated proof genuinely can't reach.
> [!IMPORTANT]
> Different kinds of evidence are never blended into one deceptively simple percentage. An automated proof, a reviewed judgment call, and a fallback are always reported as **separate counts** — `percent_complete` counts only automated proof.
## The 5 tools
| Tool | Purpose |
|---|---|
| `project_define` | Turn a goal into interrogated, self-audited, traceable acceptance criteria |
| `project_check` | Run real verification for one criterion — FAIL_TO_PASS/PASS_TO_PASS + mandatory mutation-checking |
| `project_status` | Read-only progress report, with each kind of evidence counted separately |
| `project_list` | Discover every tracked project's id + a compact status summary — no args needed |
| `project_get` | Read back a project's full stored definition, including every criterion's given/when/then and grounding |
`project_list` and `project_get` exist because of a real gap found in production use: `project_status`/`project_check` both require an exact `project_id` with no way to discover what IDs exist, and `project_status` only ever reports which bucket a criterion falls into — never the actual requirement text behind it. Any fresh session, or a second client with no memory of the original `project_define()` call, needs both to recover context.
## The core loop
```
1. project_list() discover what project_ids exist
2. project_define(project_id, goal, dod, ...) interrogate a goal into real criteria
3. project_check(project_id, criterion_id, ...) verify one criterion, repeated as work happens
4. project_get(project_id) recall a criterion's given/when/then
5. project_status(project_id) read the honest, unblended percentage
```
### 1 · `project_define` — interrogation before invention
Turns a vague goal into Given-When-Then acceptance criteria, but only after real interrogation:
- Every criterion must trace back to something the user actually said (`grounding` field) — nothing is invented from nothing.
- Every criterion is self-audited on two axes before submission: how underspecified it is, and how gameable it is. Score too high on either and it's rejected.
- If more than half the submitted criteria fail this audit, the **whole batch** is rejected — that signals the interrogation itself was too shallow, not that individual criteria need patching.
- Given-When-Then text is scanned for unquantified adjectives ("secure," "fast," "robust," ...) — rejected because they're not measurable, whole-word matched so it doesn't false-positive on things like "fastener."
- Duplicate criterion IDs, empty submissions, and a missing/non-string `project_id` are all rejected outright; malformed input produces a clean structured error, never a crash.
- `reviewer_level` picks which persona `claude_review` must embody for this project's whole lifetime.
### 2 · `project_check` — the load-bearing tool
Runs actual verification for one criterion, called once per criterion as real work happens:
- **FAIL_TO_PASS / PASS_TO_PASS verification** (SWE-bench style): a test must be shown to have failed before the fix and pass after — either via a real pre-change command re-run, or an explicit, trusted-not-verified assertion.
- **Mutation checking is mandatory before a criterion's first real verification.** Rework deliberately breaks the target code and confirms the test actually catches the break. A test that can't catch a broken implementation isn't real verification — it might be tautological. The engine tries, in order: comparators (`==`, `<`, ...), unary negation (`!x`), and — only as a fallback — binary arithmetic (`+ - * /`), so a pure-arithmetic function like `return a + b` is still mutation-checkable instead of always routing to review.
- **Non-functional-risk gating**: criteria flagged security/crypto/concurrency never pass on a plain green test alone — they require an explicit property test or human review.
- Editing the test file itself to make it pass triggers an automatic fail — the classic way agents cheat verification.
- When mutation-checking can't cleanly resolve, the tool doesn't guess — it routes to `human_review` or `claude_review`.
> [!NOTE]
> `claude_review` is a real evaluation, not a rubber stamp. It requires an actual verdict (`trustworthy` / `not_trustworthy`) plus specific written reasoning that meets the project's `reviewer_level` bar. A `trustworthy` verdict reaches its own status, `verified_complete_reviewed` — kept deliberately separate from automated `verified_complete` so a judgment call is never confused with a proof.
#### Language support
| Extensions | Checker | Status |
|---|---|---|
| `.py` | tokenize-based | Fully proven — the original path, most-tested |
| `.ts .tsx .js .jsx` | hand-tuned tree-sitter | Fully proven — real compile/run cycles, all function forms |
| `.go .rs .java .c .h .cpp .cc .hpp .cs` | tree-sitter-language-pack | **Java** proven end-to-end; the rest are structurally verified but not proof-of-execution verified |
| anything else | — | A clear "no checker supports this extension" reason, never a silent skip |
#### Reviewer levels
`reviewer_level` doesn't change any code path — it changes the bar the reviewing agent must hold itself to, and enforces a rising **minimum length** on `claude_review_reasoning` so a rubber-stamp verdict can't sneak through:
| Level | Bar | Enforced minimum |
|---|---|---|
| `standard` | A normal careful reviewer | none |
| `skeptical` | Defaults to `not_trustworthy` unless specific evidence overturns it | 40 characters |
| `foreman` | Truth over comfort — no rubber stamps, calls out vague claims and "started" vs. "done" conflation by name | 80 characters |
### 3 · `project_status` — the honest number
Read-only, safe to call anytime. `percent_complete` is calculated strictly from automated `verified_complete` criteria — never blending in `verified_complete_reviewed` (judgment calls) or `fallback_complete` (env errors, human-approved fallbacks). Criteria with corrupted/unrecognized status values are surfaced with a warning instead of silently vanishing from the count.
### 4 · `project_list` — discovery
Takes no arguments. Returns every tracked project's `project_id`, `goal`, `reviewer_level`, and a compact status summary. A corrupted project file is reported in its own `errors[]` list rather than crashing the whole call.
### 5 · `project_get` — recall
Returns a project's full stored definition — `goal`, `dod`, `reviewer_level`, and every criterion in full: `feature`, `given_when_then`, `grounding`, `self_audit`, `non_functional_risk`, `status`, and the complete verification record, including any `claude_review` reasoning.
## Desktop dashboard
`ui/` is an optional Tauri + React desktop app: a read-only status view over every tracked project, plus a **Review** button on each criterion that opens a real terminal running `claude` with a fully-briefed audit prompt — goal, given/when/then, grounding, current status, prior fallback/review history, and a pointer to `AGENT.md` — not a bare "check this" one-liner.
```powershell
./setup.ps1 -InstallUI
```
This builds a real Windows installer and installs it silently (no admin prompt — `currentUser` mode). Rework shows up in Start Menu and Apps & Features like any normal installed program. The installed app finds your tracked projects via a small config file (`%APPDATA%\Rework\repo_root.txt`) that `setup.ps1` writes, since an installed `.exe` no longer lives near the repo the way it does in dev mode.
## Setup
### Prerequisites
| Needed for | Requirement |
|---|---|
| MCP server (always) | Python 3.10+, [Claude Code](https://claude.com/claude-code) and/or [Claude Desktop](https://claude.ai/download) |
| Desktop dashboard (optional) | + [Node.js](https://nodejs.org) and the [Rust toolchain](https://rustup.rs) — only needed if you pass `-InstallUI` or build `ui/` yourself |
### Backend — Windows, one command
```powershell
./setup.ps1
```
This creates Rework's own venv, installs dependencies, registers the MCP server with Claude Code (`claude mcp add`, user-scoped) and Claude Desktop (auto-detects both legacy and packaged/MSIX-style install locations), installs `AGENT.md` as a Claude Code skill, and packages `rework-skill.zip` for Claude Desktop. It's idempotent — safe to re-run any time; existing `rework` registrations are replaced, not duplicated, and every other entry in either config is left untouched.
You'll be asked to pick a default `claude_review` evaluator persona (`standard` / `skeptical` / `foreman`, Enter for `standard`) — this is only a recommendation baked into the installed skill; each project still sets its own `reviewer_level` at `project_define()` time.
> [!NOTE]
> Claude Desktop has no filesystem skills folder — `setup.ps1` prepares `rework-skill.zip` and prints the one manual step: **Customize → Skills → Upload a skill** inside Claude Desktop, then select that file. Everything else in this list is fully automatic.
**After it finishes:** fully quit and reopen Claude Code and Claude Desktop (not just close the window) so both pick up the new MCP server.
### Backend — manual / other platforms
```bash
python -m venv venv
venv/Scripts/activate # or source venv/bin/activate on macOS/Linux
pip install -r requirements.txt
claude mcp add --scope user rework -- <path to venv python> <path to server.py>
```
For Claude Desktop, add the same `command`/`args` pair by hand to `mcpServers` in its config — `%APPDATA%\Claude\claude_desktop_config.json` on a traditional install, or `%LOCALAPPDATA%\Packages\Claude_<hash>\LocalCache\Roaming\Claude\claude_desktop_config.json` on a packaged/Store install.
> [!TIP]
> See [AGENT.md](AGENT.md) for the full operating guide — how to interrogate a goal, drive `project_check`, resolve mutation-check failures, choose a `reviewer_level`, and the known limitations of each language checker.
### Desktop dashboard install
The dashboard (`ui/`) is optional and off by default — building it is a multi-minute Rust release compile, so `setup.ps1` skips it unless asked.
**Install it as a real Windows app (recommended):**
```powershell
./setup.ps1 -InstallUI
```
This builds `ui/` into a signed-locally NSIS installer and runs it silently (`currentUser` mode — no admin prompt). Rework then shows up in Start Menu and Apps & Features like any normal installed program, at `%LOCALAPPDATA%\Rework`. Safe to re-run — it reinstalls over the previous version.
**Run it in dev mode instead** (no installer, live-reloads on change):
```bash
cd ui
npm install
npm run tauri dev
```
**Build the installer yourself without running it:**
```bash
cd ui
npm install
npm run tauri build -- --bundles nsis
# installer lands in ui/src-tauri/target/release/bundle/nsis/*-setup.exe
```
> [!IMPORTANT]
> Whichever path you use, the app needs to find your tracked projects. `setup.ps1` always writes `%APPDATA%\Rework\repo_root.txt` pointing at the repo root — including on a backend-only run — so a dashboard you build or install later still finds `data/projects/` correctly. An installed `.exe` lives nowhere near the repo the way `tauri dev` does, so without this file the app can't locate your data.
### Running tests
```bash
venv/Scripts/python -m pytest tests/
```
130 tests, all passing.
## Seen in action
Because Rework is a plain MCP server, any MCP client can drive it — not just Claude Code. Here's Claude Desktop given one prompt ("pick a random tracked project, show its full status and every criterion's given/when/then, then write an action plan"), using `project_list` → `project_get` → `project_status` end to end with no prior context about the project:
<p align="center">
<img src="docs/screenshots/desktop-demo-1-prompt.png" width="700" alt="Claude Desktop prompt and random project pick"><br>
<img src="docs/screenshots/desktop-demo-4-tool-calls.png" width="700" alt="Tool calls: project_list, project_get, project_status"><br>
<img src="docs/screenshots/desktop-demo-2-status-breakdown.png" width="700" alt="Status breakdown by bucket">
</p>
It correctly explains *why* two criteria sit at `verified_complete_reviewed` instead of the automated `verified_complete` (their functions have no comparator for mutation-testing to flip — regex matching and file I/O), reports the real `given_when_then` for every criterion, and — without being asked to look for it — flags that none of the project's Definition-of-Done items are actually tracked by any individual criterion:
<p align="center">
<img src="docs/screenshots/desktop-demo-5-criteria-detail.png" width="700" alt="Per-criterion given/when/then and reasoning"><br>
<img src="docs/screenshots/desktop-demo-7-action-plan.png" width="700" alt="Generated action plan">
</p>
## Proven on real work
Not just designed on paper — hardened against bugs found by actually running it:
- Tracked its own build, a bug-fix patch set, and a from-scratch **PDF-to-Markdown converter** built specifically to exercise the full loop end-to-end, including a real case where `claude_review` correctly flagged a test gap as `not_trustworthy` before it was fixed and re-verified honestly.
- Stress-tested with concurrent writes, 500-criterion payloads, 100KB strings, malformed/adversarial input, and repeated mutation cycles — a missing/non-string `project_id` used to silently write a garbage file to disk instead of being rejected, and a non-bool `non_functional_risk` used to be silently accepted despite gating on it as a plain truthy check. Both fixed.
- Found and fixed a real subprocess bug in production use: the MCP server's own long-lived stdin pipe was being inherited by every test subprocess it spawned, so a child that touched stdin for any reason could hang forever and get reported as a false `failed` — identical in symptom to a command that ran instantly outside the server. Fixed with `stdin=DEVNULL` and full process-tree termination on timeout, shared by all three mutation checkers.
## Project structure
```
server.py MCP entrypoint (FastMCP), registers the 5 tools
core/ criteria schema/validation, evaluator personas, mutation checkers, storage
tools/ project_define / project_check / project_status / project_list / project_get implementations
tests/ pytest suite (130 tests)
ui/ optional Tauri + React desktop dashboard
setup.ps1 one-shot Windows install/registration script
AGENT.md operating manual for an agent driving Rework
REF SHEET- WHEN UNSURE/ original design spec this implements
```
---
<p align="center">
Built and maintained by <strong>Santhosh Stalin</strong>.
</p>
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues