Skip to content

Releases: vkuprin/ralph-harness

v2.1.3

Choose a tag to compare

@github-actions github-actions released this 02 Oct 17:25
a5ce89b

Patch Changes

  • 7270d48: An agent keeps the git settings your environment gives through GIT_CONFIG_COUNT, GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n when the harness pushes. The setting that stops the agent's own push was written at index 0 with a count of 1, so a core.hooksPath, safe.directory or user.email set that way was gone for the agent, and only with PUSH: true or "pr". It is now added after yours.
  • 7270d48: A loop whose repository path holds a backslash no longer takes a checkout it does not own for its worktree. When WORKTREE_DIR pointed at another repository's checkout and both paths held a backslash, the ownership check passed, the agent ran there, and the harness reset and cleaned that checkout afterwards, deleting its uncommitted files. Such a start is now refused with "is not a worktree of", as it always was for ordinary paths. A repository whose path holds a backslash still runs in its own worktree.
  • 7270d48: A git fetch the loop runs is now cut off after 300 seconds the machine was awake, as its git push already was. Before, a fetch with no time limit could hang the loop for good: a connection that stalls, or a credential helper waiting on a browser window nobody sees, never fails by itself. The loop then sat between two iterations with nothing in ralph.log past "shipped", while ralph status said running. This covers the fetch when the worktree is created, the fetch before every push (PUSH: true and PUSH: "pr"), and the fetch of ralph/<name> in pull-request mode. A fetch that times out now logs "git fetch … timed out after 300s". The kept commits stay local, and the next sync tries again.
  • 7270d48: The CLI ends quietly with exit status 0 when whatever reads its output stops early: ralph status | head -1, ralph status | grep -q running, ralph review x | less quit after the first page. Before, status, results, review, log, help and the others printed a Bun stack trace (EPIPE: broken pipe, write) and exited 1, often after they had done their work, so under set -o pipefail a check like ralph status | grep -q running said a running loop was not running. A command that fails still exits 1 with its message on stderr.
  • 7270d48: With WORKTREE, the harness throws away whatever the agent left uncommitted before the gates run, so an uncommitted edit cannot help a commit pass. It ignored whether that clean worked. An agent that edited measure.sh without committing it and then made the worktree read-only got a commit kept: git reset --hard exited 128 and VERIFY_CMD ran the edited file. An agent that configured an fsmonitor hook answering "nothing changed" got the same result with every git command exiting 0. Now the clean runs with fsmonitor off, and a failed reset or clean resets the commit (revert:unclean, quoting git). A worktree that cannot be cleaned before an iteration stops the loop before the agent starts. git clean also removes a repository the agent left inside the worktree (such as a clone it looked at), which used to survive and fail the next VERIFY_CMD.
  • 7270d48: Two kinds of config.json crashed the loop before it wrote a line, and ralph start printed "started" and exited 0 with ralph.log empty: a key every JavaScript object has (toString, constructor, __proto__, …), and a RATE_LIMIT_RE and RATE_LIMIT_EXTRA_RE that each compile but not joined by | (a \k<name> in one naming a group only the other has). Both are now refused like any other setting the harness cannot read: ralph start says why and fails, and ralph new --set creates nothing.
  • 7270d48: ralph log, ralph results and ralph review refuse an n that is not a whole number of 1 or more, and print their usage. Before, ralph review <name> -5 (the tail -5 habit) said "nothing yet" over a loop's shipped commits, ralph log and ralph results printed nothing, 1e3 was read as 1, and 0 or a word as the default, every one of them with exit 0.
  • 7270d48: ralph edit reads EDITOR the way git does, as a command: EDITOR="code --wait", subl -w or emacsclient -t now open PROMPT.md. Before, the whole value was taken as the name of one program, so these, and an editor that is not installed, printed a stack trace and then held the terminal until it was killed. An editor that fails now makes ralph edit exit 1 and say so, and ralph edit on a loop that does not exist says so instead of opening an editor on a file nobody reads. An EDITOR that is the path of a program, and the default vi or notepad, run as before.
  • 7270d48: A ## or ### line inside a fenced code block in PROGRESS.md or PROMPT.md is no longer read as a heading. Before, an agent that quoted a script with a ## build comment in a Log entry broke the Log there. When PROGRESS_KEEP reached that entry, half of it went to PROGRESS-archive.md, the comment was left in PROGRESS.md as a real heading, and every older entry stayed under it with nothing ever capping it again. The same fenced line in PROMPT.md cut the job the reviewer reads at the fence, so the reviewer never saw the rules written after it. And a question under "Needs a decision" that came after such a block sent no notification. A fence that never closes is not treated as one, so a stray ``` hides no heading after it.
  • 7270d48: The frozen-file check no longer passes when git cannot run it. It read only what git diff printed and ignored the exit status, so any FROZEN entry git refused turned the check off for every entry, and the commit that edited the measurement was kept. That covers an empty entry, unknown pathspec magic such as :(bogus)x, ../x, and an absolute path, which names your checkout and not the worktree the check runs in. Now ralph start refuses such a FROZEN and quotes git's error. If the check fails during a run, the commit is reset (revert:frozen, "could not check the frozen files") and the loop stops. The reviewer is likewise no longer asked to judge an empty diff when git log or git diff fails. That iteration takes the reviewer-unavailable path. An agent that set diff.renames to a word git does not know used to cause both failures at once.
  • 7270d48: The start says when a gate you set will judge nothing. A FROZEN entry that matches no file in the worktree (Measure.sh for measure.sh, or a typo) only stops a commit that adds that file, so an edit of the file you meant shipped; the log now names the entry, and the file git has when only the case differs. With WORKTREE: false, VERIFY_CMD, REVIEW and FROZEN never run, which the log now says, and the agent is no longer told that a commit touching a frozen file is reset.
  • 7270d48: The harness finds a section of PROGRESS.md or PROMPT.md by its whole name, not by a prefix. A ## Login flow section in PROGRESS.md was read as the ## Log, so PROGRESS_KEEP counted its ### notes as entries: above the Log, real entries were moved to PROGRESS-archive.md early; below it, the notes themselves were. A ## Logging section hid the warning that the cap had no Log to count. The reviewer, handed the ## The job section, was also handed a ## The jobs table that followed it. A heading may still say more after the name (## Log (newest first)). A ## Logs heading is no longer the Log; the loop says so in ralph.log, and PROGRESS_MAX_BYTES still bounds the prompt.
  • 7270d48: A commit the gates kept stays kept when the loop is stopped right after the verdict. After the keep row the loop runs its notifier (a limit-clear or a decision event), which can take up to NOTIFY_TIMEOUT. A ralph stop, a reboot or a crash during that run used to make the next start reset ralph/<name> and set the commit aside under refs/ralph/dropped/ as "never judged", even though results.tsv called it keep. The log never said shipped, so ralph status did not count it, and with PUSH on it never reached origin. The harness now records the commit as judged, and logs it as shipped, at the moment it writes the keep row. The restart pushes the commit as it would any kept one.
  • 7270d48: LOG_KEEP: 0 now throws away every older log when the log rotates, as the template says. It used to skip the pruning, so ralph.log.1 and up, left by a higher setting, stayed for good: ralph log showed their lines and ralph status counted their iterations as this loop's, and every rotation logged "the 0 before this one are ralph.log.1 and up".
  • 7270d48: ralph migrate now refuses the lines that bash 3.2 and bash 5 read differently, instead of converting them the way one of them reads them. On a Mac, bash 3.2 is /bin/bash, and the old harness ran under whichever bash came first on PATH. A ~ after a bare name= inside a list (FROZEN=(a=~/b), (a=b:~/c)) is expanded by bash 3.2 and kept by bash 5, and ralph migrate used to keep it. A backslash, or a line continuation, at the very end of config.sh is dropped by bash 3.2, kept by bash 5.3, and left the setting unset under the bash of a macOS CI runner.
  • 7270d48: ralph migrate no longer hangs, and no longer writes a setting the old config.sh did not hold. A ; inside a list (FROZEN=(a;b)) made it run until memory ran out, about 8 GB in 8 seconds; it is now refused. Four kinds of line used to convert to something other than what bash held, and each is now refused or read as bash read it. A brace in a list (FROZEN=(src/{eval,score}.ts)) used to become one path that matches nothing, which switched the frozen-file check off for both files. A ~ after a : (PATH=/opt/bin:~/bin) or after a line continuation was kept as a literal ~. A list continued with \ at the end of each line gained empty entries, so git refused the frozen-file check's pathspec. Text glued to a list's closing ) (FROZEN=(a)#b) was dropped. A backslash at the very end of the file is now kept, as bash keeps it.
  • 7270d48: ralph new now scaffolds a loop on a linked w...
Read more

v2.1.2

Choose a tag to compare

@github-actions github-actions released this 01 Oct 19:46
9c9ca39

Patch Changes

  • 8779751: The Claude Code plugin is now its own plugin/ folder with only the ralph-new skill in it. Before, the plugin was the whole repository, and adding it could install the repository's development dependencies too. Install it the same way as before: /plugin marketplace add vkuprin/ralph-harness, then /plugin install ralph-harness@ralph-harness.

v2.1.1

Choose a tag to compare

@github-actions github-actions released this 01 Oct 19:05
7ef1b19

Patch Changes

  • 37005a2: The ralph-new setup skill can now be installed as a Claude Code plugin: /plugin marketplace add vkuprin/ralph-harness, then /plugin install ralph-harness@ralph-harness. In a Claude session it's /ralph-harness:ralph-new. Nothing changes for the CLI.

v2.1.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 20:38
7492d97

Minor Changes

  • 3432535: Windows support. ralph now runs natively on Windows 10 and 11 with bun, Git for Windows and Claude Code's native claude.exe, and CI runs the whole suite there.

    • VERIFY_CMD, HEALTH_CMD, NOTIFY_CMD and the other *_CMD settings run in Git for Windows' bash, found next to git.exe (RALPH_BASH or CLAUDE_CODE_GIT_BASH_PATH names another). The bash on a Windows PATH, usually WSL's, is not used.
    • ralph stop asks the loop to stop through a ralph.stop file, since Windows has no TERM; the loop then kills the agent's whole process tree, logs where it stopped and exits. Timeouts kill the command's tree the same way.
    • ralph status and the lock tell a loop from a stranger by its command line, read from CIM.
    • ralph tail follows the log without tail, and ralph edit falls back to notepad.
    • A loop refuses to start when the claude on PATH is npm's claude.cmd, which cannot be started without cmd.exe reading the agent's arguments.
    • The npm package can now be installed on Windows (os includes win32).

    Nothing changes on macOS or Linux.

v2.0.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 20:10
1580d58

Patch Changes

  • 6b7ac2d: Occasionally ralph start left a loop that never actually started. On Linux, bun sometimes never finishes loading the loop's code: the process is running but idle, it writes nothing to ralph.log, and ralph status shows it as running. ralph start now waits until the loop has actually started. If that hasn't happened within 30 seconds, it kills the process, notes this in ralph.log, and starts it again, up to three times.

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 17:21
2494b99

Major Changes

  • efc7d14: PUSH: true now needs "PUSH_CONFIRM": "<BRANCH>". With PUSH true every kept commit goes straight to origin/BRANCH, and whatever deploys that branch deploys the commit too. Restarting an old loop could push to production that way. A loop with PUSH: true and no matching PUSH_CONFIRM now refuses to start and tells you the line to add, and ralph new won't scaffold one. New loops from the template use PUSH: "pr".

    Also new:

    • PR_DRAFT (on in the template): the loop's pull request stays a draft while the loop runs, and it's marked ready when the loop ends by itself. If you give each stage of a job its own loop, the pull request matches the stage, and GitHub won't let anyone merge it halfway.
    • LAND_OK_CMD: your own check that BRANCH can move now, for example "no ingest run is in progress". While it fails, a push with PUSH: true or a merge with PR_MERGE waits, and you get one land-held notification.
    • The reviewer now sees each commit's message, which it couldn't read from git inside a worktree. It's also told which checks already passed (VERIFY_CMD and its output, frozen files), so it no longer rejects a commit only because it couldn't run the tests itself.
    • The agent is told that the harness runs VERIFY_CMD after each iteration, so it doesn't run the full check a second time itself. When VERIFY_CMD fails, the next prompt includes the last lines of its output and the git cherry-pick range that restores the reset commits.

v1.0.0

Choose a tag to compare

@vkuprin vkuprin released this 28 Sep 15:16
dfadf20

The first published release. Install with brew install vkuprin/tap/ralph or
bun add -g @vkuprin/ralph-harness.

  • A Ralph loop for Claude Code that runs for days: every iteration is a fresh
    claude -p that reads PROMPT.md and PROGRESS.md, and nothing but files
    crosses iterations.
  • Git, not the model, decides what shipped. Each new commit goes through the
    FROZEN check, VERIFY_CMD and an optional read-only reviewer. A commit
    that fails is reset, one that passes is pushed.
  • WORKTREE gives each loop its own worktree and branch ralph/<name>, so the
    agent never touches your checkout. PUSH: "pr" keeps one pull request up to
    date, and PR_MERGE merges it when the loop ends by itself and every check
    passes.
  • PLAN_FIRST starts each iteration in plan mode. The harness approves the
    plan itself, then the same run carries it out.
  • ralph setup opens Claude Code with the ralph-new skill, which asks how the
    loop should run and scaffolds it with ralph new --set.
  • Other commands: ralph start, stop, status, review, results, log,
    tail, steer (redirect a running loop mid-iteration), edit, migrate
    (from the old bash harness's config.sh) and --version.
  • Usage limits are waited out, an iteration that ships nothing backs off
    rather than stopping, and timeouts count only the time the machine was
    awake.