Releases: vkuprin/ralph-harness
Releases · vkuprin/ralph-harness
Release list
v2.1.3
Patch Changes
- 7270d48: An agent keeps the git settings your environment gives through
GIT_CONFIG_COUNT,GIT_CONFIG_KEY_nandGIT_CONFIG_VALUE_nwhen the harness pushes. The setting that stops the agent's own push was written at index 0 with a count of 1, so acore.hooksPath,safe.directoryoruser.emailset that way was gone for the agent, and only withPUSH: trueor"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_DIRpointed 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 fetchthe loop runs is now cut off after 300 seconds the machine was awake, as itsgit pushalready 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 inralph.logpast "shipped", whileralph statussaid running. This covers the fetch when the worktree is created, the fetch before every push (PUSH: trueandPUSH: "pr"), and the fetch ofralph/<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 | lessquit after the first page. Before,status,results,review,log,helpand the others printed a Bun stack trace (EPIPE: broken pipe, write) and exited 1, often after they had done their work, so underset -o pipefaila check likeralph status | grep -q runningsaid 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 editedmeasure.shwithout committing it and then made the worktree read-only got a commit kept:git reset --hardexited 128 andVERIFY_CMDran 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 cleanalso removes a repository the agent left inside the worktree (such as a clone it looked at), which used to survive and fail the nextVERIFY_CMD. - 7270d48: Two kinds of
config.jsoncrashed the loop before it wrote a line, andralph startprinted "started" and exited 0 withralph.logempty: a key every JavaScript object has (toString,constructor,__proto__, …), and aRATE_LIMIT_REandRATE_LIMIT_EXTRA_REthat 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 startsays why and fails, andralph new --setcreates nothing. - 7270d48:
ralph log,ralph resultsandralph reviewrefuse annthat is not a whole number of 1 or more, and print their usage. Before,ralph review <name> -5(thetail -5habit) said "nothing yet" over a loop's shipped commits,ralph logandralph resultsprinted nothing,1e3was read as 1, and0or a word as the default, every one of them with exit 0. - 7270d48:
ralph editreadsEDITORthe way git does, as a command:EDITOR="code --wait",subl -woremacsclient -tnow openPROMPT.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 makesralph editexit 1 and say so, andralph editon a loop that does not exist says so instead of opening an editor on a file nobody reads. AnEDITORthat is the path of a program, and the defaultviornotepad, run as before. - 7270d48: A
##or###line inside a fenced code block inPROGRESS.mdorPROMPT.mdis no longer read as a heading. Before, an agent that quoted a script with a## buildcomment in a Log entry broke the Log there. WhenPROGRESS_KEEPreached that entry, half of it went toPROGRESS-archive.md, the comment was left inPROGRESS.mdas a real heading, and every older entry stayed under it with nothing ever capping it again. The same fenced line inPROMPT.mdcut 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 diffprinted and ignored the exit status, so anyFROZENentry 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. Nowralph startrefuses such aFROZENand 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 whengit logorgit difffails. That iteration takes the reviewer-unavailable path. An agent that setdiff.renamesto 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
FROZENentry that matches no file in the worktree (Measure.shformeasure.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. WithWORKTREE: false,VERIFY_CMD,REVIEWandFROZENnever 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.mdorPROMPT.mdby its whole name, not by a prefix. A## Login flowsection inPROGRESS.mdwas read as the## Log, soPROGRESS_KEEPcounted its###notes as entries: above the Log, real entries were moved toPROGRESS-archive.mdearly; below it, the notes themselves were. A## Loggingsection hid the warning that the cap had no Log to count. The reviewer, handed the## The jobsection, was also handed a## The jobs tablethat followed it. A heading may still say more after the name (## Log (newest first)). A## Logsheading is no longer the Log; the loop says so inralph.log, andPROGRESS_MAX_BYTESstill 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-clearor adecisionevent), which can take up toNOTIFY_TIMEOUT. Aralph stop, a reboot or a crash during that run used to make the next start resetralph/<name>and set the commit aside underrefs/ralph/dropped/as "never judged", even thoughresults.tsvcalled itkeep. The log never saidshipped, soralph statusdid not count it, and withPUSHon 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: 0now throws away every older log when the log rotates, as the template says. It used to skip the pruning, soralph.log.1and up, left by a higher setting, stayed for good:ralph logshowed their lines andralph statuscounted their iterations as this loop's, and every rotation logged "the 0 before this one are ralph.log.1 and up". - 7270d48:
ralph migratenow 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 onPATH. A~after a barename=inside a list (FROZEN=(a=~/b),(a=b:~/c)) is expanded by bash 3.2 and kept by bash 5, andralph migrateused to keep it. A backslash, or a line continuation, at the very end ofconfig.shis 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 migrateno longer hangs, and no longer writes a setting the oldconfig.shdid 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 newnow scaffolds a loop on a linked w...
v2.1.2
Patch Changes
- 8779751: The Claude Code plugin is now its own
plugin/folder with only theralph-newskill 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
Patch Changes
- 37005a2: The
ralph-newsetup 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
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_CMDand the other*_CMDsettings run in Git for Windows' bash, found next togit.exe(RALPH_BASHorCLAUDE_CODE_GIT_BASH_PATHnames another). Thebashon a Windows PATH, usually WSL's, is not used.ralph stopasks the loop to stop through aralph.stopfile, 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 statusand the lock tell a loop from a stranger by its command line, read from CIM.ralph tailfollows the log withouttail, andralph editfalls back tonotepad.- A loop refuses to start when the
claudeon PATH is npm'sclaude.cmd, which cannot be started without cmd.exe reading the agent's arguments. - The npm package can now be installed on Windows (
osincludeswin32).
Nothing changes on macOS or Linux.
v2.0.1
Patch Changes
- 6b7ac2d: Occasionally
ralph startleft 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 toralph.log, andralph statusshows it as running.ralph startnow waits until the loop has actually started. If that hasn't happened within 30 seconds, it kills the process, notes this inralph.log, and starts it again, up to three times.
v2.0.0
Major Changes
-
efc7d14:
PUSH: truenow needs"PUSH_CONFIRM": "<BRANCH>". With PUSH true every kept commit goes straight toorigin/BRANCH, and whatever deploys that branch deploys the commit too. Restarting an old loop could push to production that way. A loop withPUSH: trueand no matchingPUSH_CONFIRMnow refuses to start and tells you the line to add, andralph newwon't scaffold one. New loops from the template usePUSH: "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 thatBRANCHcan move now, for example "no ingest run is in progress". While it fails, a push withPUSH: trueor a merge withPR_MERGEwaits, and you get oneland-heldnotification.- 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_CMDand 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_CMDafter each iteration, so it doesn't run the full check a second time itself. WhenVERIFY_CMDfails, the next prompt includes the last lines of its output and thegit cherry-pickrange that restores the reset commits.
v1.0.0
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 -pthat readsPROMPT.mdandPROGRESS.md, and nothing but files
crosses iterations. - Git, not the model, decides what shipped. Each new commit goes through the
FROZENcheck,VERIFY_CMDand an optional read-only reviewer. A commit
that fails is reset, one that passes is pushed. WORKTREEgives each loop its own worktree and branchralph/<name>, so the
agent never touches your checkout.PUSH: "pr"keeps one pull request up to
date, andPR_MERGEmerges it when the loop ends by itself and every check
passes.PLAN_FIRSTstarts each iteration in plan mode. The harness approves the
plan itself, then the same run carries it out.ralph setupopens Claude Code with theralph-newskill, which asks how the
loop should run and scaffolds it withralph 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'sconfig.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.