Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.2.0a1 — 2026-10-21

- Subscription replay through installed Codex and Claude CLIs, with native login and no API fallback.
- Sequential trusted local workspaces, baseline/reference controls, and offline Docker verification.
- Frozen rules comparisons, durable attempt receipts, and subscription-aware cost labels.
- Explicit tool-profile and host-isolation limitations; errors stop further allowance use.

## 0.1.0a1 — 2026-10-20

- Local failure capture, request selection and interactive Harbor task authoring.
Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Local capture. No telemetry. Open source. Task authoring and model replay use yo
**Python 3.11+ and Git.** Install the published alpha with [uv](https://docs.astral.sh/uv/):

```bash
uv tool install https://github.com/alexdevmotion/grudges/releases/download/v0.1.0a1/grudges-0.1.0a1-py3-none-any.whl
uv tool install https://github.com/alexdevmotion/grudges/releases/download/v0.2.0a1/grudges-0.2.0a1-py3-none-any.whl
grudges --help
```

Expand Down Expand Up @@ -92,6 +92,21 @@ Building opens an interactive authoring session. **Review the reconstructed task

Capture preserves your working tree, normal Git index and branches. Missing checkpoints are labeled approximate. To remove the managed hooks and unchanged managed skills: `grudges install --uninstall`.

## Replay with the subscription you already have

Sign in with `codex login` or `claude auth login`, then run a reviewed task:

```bash
grudges run path/to/task --runner local-codex --trust-local -k 1
grudges run path/to/task --runner local-claude --trust-local -m sonnet -k 1
# Compare the same task with your project rules:
grudges run path/to/task --runner local-codex --trust-local --rules ./AGENTS.md -k 3
```

No API key required. Grudges invokes your installed CLI with its existing login; credentials stay with the CLI. Runs consume your plan's allowance, execute sequentially, and stop on runner errors. Docker checks the baseline and reference solution before the first model attempt, then grades each candidate offline.

**Trusted local mode:** agents run on your host. Codex has a workspace shell; Claude uses restricted file-editing tools. Hidden tests are kept outside the working directory, but this is not a security boundary or a fair cross-agent benchmark. [Requirements and limitations](docs/usage.md#subscription-runners).

## Replay the same task. Change one thing.

Set `OPENAI_API_KEY` in your environment for Codex replay. Use model IDs available to your account; live runs incur provider charges.
Expand All @@ -110,22 +125,22 @@ grudges run path/to/task -a codex \
-m YOUR_MODEL_ID --rules ./AGENTS.md -k 3
```

The two arms are **as captured** and **with rules**. Existing baseline rules are retained; the second copy replaces the root harness rules file. Inputs are frozen before execution. Native Codex and Claude Code replay use API keys, not subscription logins. See [supported rules layouts and behavior](docs/usage.md#try-a-pack).
The two arms are **as captured** and **with rules**. Existing baseline rules are retained; the second copy replaces the root harness rules file. Inputs are frozen before execution. The default Harbor runner uses API keys. Choose a local runner above to use your subscription. See [supported rules layouts and behavior](docs/usage.md#try-a-pack).

## Scores you can inspect

| What you see | What it means |
| --- | --- |
| Solved / scored | Successful attempts out of valid scored attempts; timeouts count as failures. |
| Solved / scored | Successful attempts out of valid scored attempts; Harbor agent timeouts count as failures. |
| Errors & missing | Provider, grader and infrastructure errors, plus planned attempts with no receipt. |
| Majority-fixed | Every planned attempt was scored and strictly more than half solved the task. |
| Cost | Reported usage, with unavailable or partial totals labeled. |

Each run keeps `results.json`, the HTML card and Harbor receipts locally. A whole-task score doesn't tell you which requirement failed; inspect the verifier output. You can [set public titles and regenerate a card](docs/usage.md#read-results) before sharing it.
Each run keeps `results.json`, the HTML card and runner receipts locally. A whole-task score doesn't tell you which requirement failed; inspect the verifier output. You can [set public titles and regenerate a card](docs/usage.md#read-results) before sharing it.

## Built on tools that already work

Grudges handles capture, replay comparisons and reporting. [evals-from-sessions](https://github.com/ibragim-bad/evals-from-sessions) supplies the task-authoring workflow; [Harbor](https://github.com/harbor-framework/harbor) runs the tasks.
Grudges handles capture, replay comparisons and reporting. [evals-from-sessions](https://github.com/ibragim-bad/evals-from-sessions) supplies the task-authoring workflow; [Harbor](https://github.com/harbor-framework/harbor) runs API tasks; installed Codex and Claude CLIs run subscription tasks.

The example packs include [eight explicit tasks](packs/starter/README.md) and [six intent tasks](packs/starter-intent/README.md). Their fairness notes distinguish stated requirements from inferred behavior. [Release checks](RELEASE-CHECKS.md) document what has actually been validated.

Expand Down
17 changes: 17 additions & 0 deletions RELEASE-CHECKS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Local release validation — 0.2.0a1

21 October 2026. Linux; GitHub prerelease preparation.

- Python 3.11 and 3.13: 78 tests passed, one optional external-receipt test skipped.
- All 14 bundled tasks passed local baseline/reference controls (28 offline Docker verifier runs): broken inputs fail and reference solutions pass through the actual local-runner mounts and UID.
- Live subscription replay passed for Codex CLI 0.163.0 (`gpt-6.1-sol`) and Claude Code (`sonnet`). Each completed a one-attempt, two-setup comparison on `starter/blur-formatting`.
- Both rules canaries were absent from baseline candidates and present only in the rules candidates; all four final task attempts solved. This verifies rule delivery and replay mechanics, not rule effectiveness or model quality.
- Restricted Claude did not automatically load root `CLAUDE.md` in direct canary tests. The runner now explicitly includes it in the prompt; unit and live tests cover that behavior.
- Review fixes include reward-file grading despite nonzero verifier exit, writable disposable verification copies, host UID permissions, paired setup ordering, subscription receipts, timing, interruption and conservative error handling.
- Desktop (1280px) and mobile (390px) cards checked in a browser; no page overflow at 390px. Subscription cost and trusted-host limitations remain visible.
- Release packaging and fresh installed-wheel checks cover both packs and CLI entry points. Source secret scan passed. Raw model transcripts and credentials are excluded from the release.

Subscription tests consumed plan allowance. Local runners use different tool profiles and do not isolate the agent from the host; they are not equivalent to isolated Harbor evaluations. API replay remains available. Local subscription mode supports Linux and the documented source-task layout only.

---

# Local release validation — 0.1.0a1

20 October 2026. Linux only. Pre-publication validation record. See the GitHub release for publication status.
Expand Down
16 changes: 8 additions & 8 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Release checklist

Target: `alexdevmotion/grudges`, version `0.1.0a1`. The owner authorized the initial GitHub release. Future releases require owner approval. GitHub publication does not authorize uploading to PyPI or sending launch messages.
GitHub releases require owner authorization. PyPI publication and launch messages require separate authorization.

1. Run unit tests, build wheel and sdist, and run `scripts/check_dist.py`. Install the wheel in a fresh environment outside the checkout; verify `--version`, capture and both bundled packs.
2. Run nop/oracle controls and inspect individual receipts. Check desktop and narrow-screen cards, including errors, missing attempts and more than six configurations.
3. Scan candidate files and the intended public Git history for secrets. The prepared source ZIP excludes Git history. Use it for a fresh public initial import after approval, preserving the original development repository privately. Do not push the old development history: it contains attribution trailers prohibited by the repository policy. Do not publish private research, sessions, receipts or the historical rules-trial card.
4. Review NOTICE, task fairness notes, README commands and platform limitations. Configure a private vulnerability-reporting channel and repository rules under the owner's account.
5. After approval, create/push the reviewed public history, tag the exact release commit, and attach the checked wheel/sdist with SHA-256 checksums to the release. PyPI publication requires separate account/name verification and authorization; no automatic publishing workflow is configured.
6. Test installation from the actual published artifact before announcing it. Record the public commit, artifact hashes and smoke-test results.
1. Run tests on Python 3.11 and 3.13. Build wheel and sdist; run `scripts/check_dist.py`. Install the wheel outside the checkout and verify the CLI and both bundled packs.
2. Check baseline/reference controls and individual rewards. For runner changes, test the affected live CLI, including a rules canary. Do not infer model quality from these smoke tests.
3. Check cards at desktop and mobile widths. Keep errors, missing attempts and unavailable costs visible.
4. Scan release files and the intended public Git history for secrets. Never include captures, raw sessions, credentials or private research. Preserve the original development checkout and its history.
5. Update the version, changelog, README install URL and `RELEASE-CHECKS.md`. Open a focused PR; require passing CI before merge.
6. Tag the exact merged commit. Build artifacts from that commit, attach wheel/sdist and SHA-256 checksums to a GitHub prerelease, then download and verify them in a fresh environment.

Do not claim general model rankings from the example pack or repaired-input results from historical rescoring. External alpha usage and repaired rules-trial calibration remain separate validation steps.
The public repository is `alexdevmotion/grudges`. The alpha is distributed through GitHub, not PyPI. Keep claims limited to the documented platforms, task layouts and evidence.
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ Do not post captured sessions, secrets or exploit payloads in public issues. Rep

Treat captures, Git objects, build workspaces and replay outputs as private. Deleting a working file does not delete its snapshot. Review task code and Dockerfiles before execution. Use provider API keys with appropriate limits; do not copy consumer login files into containers. Shareable task authoring and report rendering still require human review before publication.

Local subscription runners are for trusted tasks. The agent runs on the host; a fresh workspace does not isolate it from other host files, global instructions or configuration. Claude uses restricted file tools; Codex uses its native workspace sandbox. Offline Docker verification happens afterward. Keep native login files in their normal location; Grudges does not copy them into containers.

This alpha has no security support SLA. Only the latest alpha is intended for fixes.
25 changes: 23 additions & 2 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,28 @@ grudges run --pack starter -a codex -m YOUR_MODEL_ID -k 3
grudges run path/to/task -a codex -m YOUR_MODEL_ID --rules ./AGENTS.md
```

Live runs cost money. Codex and Claude Code replay require provider API keys; subscription credentials are not forwarded. Each run freezes task copies and records its configuration before execution. Rules comparisons support the `environment/repo` plus `COPY repo/` Dockerfile layout and the Codex/Claude Code harnesses. Prebuilt `docker_image` tasks are rejected for rules comparisons because they would bypass the modified files. The comparison is **as captured** versus **with rules**: the baseline can already contain rules. The supplied text replaces the root harness rules file in the second copy; it does not remove other instruction files.
Harbor model runs incur API charges and require provider API keys. Subscription credentials are not forwarded into Harbor. Each run freezes task copies and records its configuration before execution. Rules comparisons support the `environment/repo` plus `COPY repo/` Dockerfile layout and the Codex/Claude Code harnesses. Prebuilt `docker_image` tasks are rejected for rules comparisons because they would bypass the modified files. The comparison is **as captured** versus **with rules**: the baseline can already contain rules. The supplied text replaces the root harness rules file in the second copy; it does not remove other instruction files.

## Subscription runners

Use `--runner local-codex` or `--runner local-claude` with `--trust-local`. They launch the installed official CLI on your host using its existing ChatGPT or Claude subscription login. API login is rejected, provider/key environment overrides are removed, and there is no API fallback. Grudges never reads, copies, or exports login tokens. Allowance limits still apply; cost is labeled unavailable, never $0.

```bash
codex login # or: claude auth login
grudges run path/to/task --runner local-codex --trust-local -k 1
grudges run path/to/task --runner local-claude --trust-local -m sonnet -k 1
grudges run path/to/task --runner local-claude --trust-local --rules ./CLAUDE.md -k 3
```

Requirements: Linux, Git, Docker, and a recent CLI. Codex must support `--ephemeral`; Claude must support `--restricted` and `--permission-prompts`. `-m` is optional and otherwise uses the CLI default; choose a model your plan supports. `--effort` forwards the CLI's effort setting. Local runs are sequential; `--agent` is for Harbor only.

Supported tasks have `environment/repo`, an explicit `COPY repo/ /app/`, `WORKDIR /app`, `tests/test.sh`, and `solution/solve.sh`. Prebuilt images, symlinks and nested Git metadata are rejected. Baseline must fail and the reference solution must pass using the same verifier mount arrangement before any model attempt. Tasks requiring hidden build artifacts beneath `/app` may fail this gate. The verifier must work without network access and write a binary reward to `/logs/verifier/reward.txt`.

Every attempt gets a fresh temporary source workspace. Candidates are preserved locally; verification runs in Docker with no network and a disposable writable copy. Task text referring to `/app` is mapped to the temporary working directory for the agent. Restricted Claude does not auto-load project instructions, so Grudges explicitly includes the root `CLAUDE.md` in its prompt for both baseline and rules setups; `@import` directives are not expanded and nested instruction discovery is not guaranteed. CLI versions, requested models, tool profiles and rules hashes are recorded. Claude's reported model usage is retained; Codex's requested model is not independently verified.

Local mode is for **trusted tasks only**. Codex uses its workspace sandbox and shell; Claude uses restricted Read/Edit/Write/Glob/Grep tools and cannot run tests itself. Host tools, global instructions and CLI configuration may affect results. An agent can potentially discover verifier inputs elsewhere on the host. Do not treat these results as isolated benchmarks, compare the two tool profiles as equivalent, or use this mode for hostile tasks. Credentials remain in their normal location and are not mounted into Docker.

Authentication, quota and incomplete-output errors stop the queue, preserve receipts, and leave remaining attempts missing. Local timeouts are unscored infrastructure errors because a timeout alone cannot distinguish model work from provider retries. No automatic retry spends additional allowance.

## Capture your own failure

Expand All @@ -45,7 +66,7 @@ Task building fetches a pinned [evals-from-sessions](https://github.com/ibragim-

Each run writes `results.json` and `card.html` under `~/.grudges/runs/`. `grudges card` renders the latest run, or pass a run directory. `--titles titles.json` supplies public task titles.

Cards show solved/scored attempts, errors and missing attempts. A task is majority-fixed only when all planned attempts are scored and strictly more than half solve it. Agent timeouts count as scored failures; provider, infrastructure and grader errors do not. Partial cost totals are labeled. A whole-task reward cannot identify which individual requirement failed. Inspect verifier receipts for that detail. CLI exit 2 signals setup/execution errors; a valid scored task failure returns 0.
Cards show solved/scored attempts, errors and missing attempts. A task is majority-fixed only when all planned attempts are scored and strictly more than half solve it. Harbor agent timeouts count as scored failures; local timeouts remain unscored; provider, infrastructure and grader errors do not. Partial cost totals are labeled. A whole-task reward cannot identify which individual requirement failed. Inspect verifier receipts for that detail. CLI exit 2 signals setup/execution errors; a valid scored task failure returns 0.

## Privacy and limits

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "grudges"
version = "0.1.0a1"
version = "0.2.0a1"
description = "Turn coding-agent failures into replayable tests. Compare models and project rules."
readme = "README.md"
license = "Apache-2.0"
Expand Down
8 changes: 6 additions & 2 deletions src/grudges/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _tally(n_tasks, fixed, width=560, height=64):
return f'<svg viewBox="0 0 {width} {height}" class="tally" aria-hidden="true">{"".join(parts)}</svg>'


HARNESS_NAMES = {"codex": "Codex", "claude-code": "Claude Code", "opencode": "OpenCode", "terminus-2": "Terminus"}
HARNESS_NAMES = {"local-codex": "local Codex", "local-claude": "local Claude Code", "codex": "Codex", "claude-code": "Claude Code", "opencode": "OpenCode", "terminus-2": "Terminus"}


def _who(label):
Expand All @@ -42,7 +42,9 @@ def _who(label):

def _spend(s):
parts = []
if s.get("cost_usd") is not None:
if s.get("billing") == "subscription":
parts.append("subscription usage · USD cost unavailable")
elif s.get("cost_usd") is not None:
parts.append(f"${s['cost_usd']:.2f}" + (" known cost (partial)" if s.get("cost_complete") is False else ""))
else:
parts.append("cost unavailable")
Expand Down Expand Up @@ -118,6 +120,8 @@ def rank(label):
subject = f"{_n(n_tasks, 'task')} my coding agent failed"
footer = (f"{expected} planned attempts per task/setup. Errors excluded from scored; missing = planned but unreported. "
"Majority-fixed = all planned attempts scored and more than half solved.")
if result.get("runner", "").startswith("local-"):
footer += " Trusted local CLI run; host configuration may affect results. Not equivalent to isolated Harbor runs."
body = []
for i, t in enumerate(tasks + [None] * (n_tasks - len(tasks)), 1):
cells = []
Expand Down
Loading
Loading