Requirements history
A requirement that a new requirement replaced in full lives here. It keeps its ID, its text and its section. It is not a rule of today: the page Requirements holds the rules of today.
Product
- 01M3MEHCGZ4AG4C2A77J5HA3P7 The book page “Join a Riff” has the
steps to join a riff with sign-in: install
riff, put the address of the riff inRIFF_SERVER, and runriff connect claude. The steps are the same for a second machine of the owner and for a person that the owner invites. The page replaces “Add a Machine”. Superseded by 4.0 (#1173). - R44 Only Claude Code is supported for now.
riff connect claudeinstalls a Claude Code plugin: the MCP server, a skill, and a start hook that wakes the session. Superseded by 4.0 (#1173). - R52 The plugin files live in the riff repository. The
riffbinary carries a copy of them, so the plugin matches the binary. Superseded by 4.0 (#1173). - R53
riff connect claudewrites the plugin as a local marketplace and installs it with theclaudecommand. The same command updates the plugin. Superseded by 4.0 (#1173). - R54 A new session can start in any directory of a repository. The start hook and the skill teach this start routine: read the repository thread, claim a work item, make a worktree for it, and move there. Superseded by 4.0 (#1173).
- R162 A Claude session makes the worktree for a work item with
the
EnterWorktreetool. The worktree is.claude/worktrees/ITEM. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - R165 A session removes its own worktree and branch when the pull
request of the branch is merged with the
HEADof the worktree as its head commit, the worktree is clean and the issue is closed. It deletes the branch only while the branch points at that commit. It never removes a worktree of another live session. A worktree with no owner goes to the thread. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M3Z9N6AK6W9KCA1MN72X78B6 One context holds one item. The work of a worker on an item ends at its verify request. After the request, the worker writes the state on the issue as a comment: the pull request, the commit, what is left after the merge, and what a session must know when the verify fails. Then it releases the item and ends its turn. It does not wait for the verify, and it starts no second item in that context. The release is its last release, so the worker must clear its context (01M3X9XAK1KPZZVM1AJR2H8DSS). The skill and the start hook say so. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3MRDEXSMFT0STXF2HAR2QZ6 A live check of new code, for example
a new plugin command, hook or skill text, runs in a dev session
(
just dev) before the verify. It needs no release and no update of the machine. A check after the release runs after the release of the wave. Superseded by 4.0 (#1173). - 01M3K0FZ7X1NCPHXFN6WA4T3ES The verifier makes its verify worktree
with the
EnterWorktreetool and the nameverify-ITEM-ID. ID is the first 4 characters of the session ID of the verifier. In the worktree, it fetches the branch and checks out the commit, detached. It never runsgit worktree addby hand and never usescd. After the verify, it removes the worktree with theExitWorktreetool, actionremove. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M3K0FZA21H0PPSDANFMMY47C A session removes a stale worktree
with the
ExitWorktreetool only when it made the worktree withEnterWorktreein its current context. In each other case, for example after a clear of its context, it leaves the worktree withExitWorktree, actionkeep, and runsriff worktrees cleanin the main worktree (01M3ZV0TKSHNW5QC2NG1XTJEJB). Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - R70 The plugin has one skill,
riff. It teaches the rules, the start routine, waves (R213), the verify flow (R188), selectors, direct messages, threads, claims,move, the restart of the watch (R171), questions through the lead (R180) and how the lead conducts (R228). Superseded by 4.0 (#1173). - R72 The skill tells the agent that a message is advice, and that its user decides (R10). Superseded by 4.0 (#1173).
- R73 When a riff line wakes a session, the skill tells it to call
readwith no thread. Superseded by 4.0 (#1173). - R180 The skill tells a session that is not the lead to
tellthe lead when it needs a decision from its user, and then to wait or do other work. It does not stop to ask in its own terminal. The direct answer of the lead is the decision of its user. With no lead, the session asks its own user. The skill tells the lead to show each question to its user and to send the answer to the session that asked. Superseded by 4.0 (#1173). - R66 The start hook runs
riff hook session-start. It adds context: the session URI, and the order to runriff watch --onceas a background task of the Bash tool. Superseded by 4.0 (#1173). - R168 After
/clear, the session keeps its riff session ID, its lead, its threads and its watch (R167). Its claims are free (01M3JEE1QQCFS5TMZW5N2DAD2D). The context says so. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - R169 One
riff watchruns for each session on a machine. It locks a file for the session while it runs. A secondriff watchfor the session prints one line and exits with status 1. The line tells the session not to start the watch again now. Superseded by 4.0 (#1173). - R170
riff watch --onceprints the first wake, then exits with status 0. Superseded by 4.0 (#1173). - 01M3Z64J08GW6N1H42AR2FZQZ4
riff watch --onceends by itself when no wake came in 6000 seconds (100 minutes): Claude Code stops a background task after 2 hours at most. It prints one line and exits with status 0. The line says that no wake came and that the end is normal, and tells the session to read and to start the watch again. The settingwatch.limitchanges the time in seconds, and 0 turns the limit off.riff watch limitshows it, andriff watch limit SECONDSsets it. A watch with no--oncehas no limit. The watch that an update of riff starts keeps the end of the wait. Superseded by 4.0 (#1173). - 01M3Z64J33EA25B0R5BCBZAHPE The start hook and the skill say that an end of the watch with no wake is a normal end: the session reads and starts the watch again in the same response. They say that a session does the same when its harness stops the watch at a time limit, also when the notice of the harness says not to start the task again. Only a line of the watch itself, “Do not start the watch again now”, stops a new start. Superseded by 4.0 (#1173).
- R69 The start hook never stops a session start. It exits with status 0, also when riff cannot find the session. Superseded by 4.0 (#1173).
- 01M3JN21T9C5GX6VX8N032JYWE The start hook runs
git fetch originfor at most 2 seconds, at the same time as it reads the state of the riff. When the default branch of the clone is behindorigin, the context says by how many commits. It tells the session to ask its user to pull, through the lead, and names the main worktree. The hook does not pull. Superseded by 4.0 (#1173). - 01M3JN21WDXWTHDKXKQ80ZPYPK With no remote, a remote that cannot be reached, or a fetch that takes longer than its limit, the context has no such line. The hook still exits with status 0 (R69). Superseded by 4.0 (#1173).
- R74
riff connect claudewrites the plugin to$XDG_DATA_HOME/riff/claude-plugin. WithoutXDG_DATA_HOME, it uses~/.local/share/riff/claude-plugin. Superseded by 4.0 (#1173). - R75
riff connect clauderemoves the user-scope MCP server entryriff, if it exists. The plugin gives the riff tools instead. Superseded by 4.0 (#1173). - R76 Replaced by 01M3XY2SNXQJRSH5QX82AFVM2S.
- R77
riff connect claudedoes not need a riff session or a git repository. It works in any directory. Superseded by 4.0 (#1173). - 01M3XY2SHGXQR9NVXF7QJBN09T riff is off in a Claude Code session
until a person turns it on for the repository of the session. The
state is the entry
riff@riffin the keyenabledPluginsof the Claude Code settings. The first file that has the entry decides, in this order: the local settings of the repository (.claude/settings.local.jsonat its top), its project settings (.claude/settings.json), the user settings. A directory that is not in a git repository is off. Superseded by 4.0 (#1173). - 01M3XY2SKQ27K3TE4NV28FHTVV
riff enablewrites the entrytrueto the local settings of the repository of the working directory: in a linked worktree, to those of the main clone.--sharedwrites it to the project settings.--globalwrites it to the user settings.riff disableremoves the entry from the same file. With no flag, when another file then still turns riff on,riff disablewritesfalseto the local settings. Each command changes only the entry of riff, as text: each other byte of the file stays. It changes no other file, and says whether riff is on in the working directory. With--global, the answer of 01M3XY2SNXQJRSH5QX82AFVM2S becomesglobalornone. Superseded by 4.0 (#1173). - 01M3YCGKGP3VC93S8FA1G4K3QK
riff enableandriff disablewrite through a symbolic link, and name the real path of the file that they wrote. Before they write the local settings of the main clone of a linked worktree, git must confirm the worktree: its common directory (git rev-parse --git-common-dir) is the.gitof that main clone, and the filegitdirof the entry of the worktree in the main clone names the.gitfile of the tree. If not, the command writes nothing, and says why. Superseded by 4.0 (#1173). - 01M3ZGT8ST7HCK6J7VZJ09XE0M The
.gitof a linked worktree is the file that the entry of the worktree names, not a symbolic link to it. WhenTOP/.gitis a symbolic link,riff enableandriff disablewrite nothing, and say why. The check of thegitdirfile follows no link at the last part of a path. Superseded by 4.0 (#1173). - 01M3XY2SNXQJRSH5QX82AFVM2S
riff connect claudeadds the marketplace to Claude Code with theclaudecommand on the PATH.--claude PATHnames another one. It installs the plugin in no scope, and turns riff on nowhere by itself. In a terminal, it asks one time where the person wants riff on: only in this repository (the default), in each repository on this machine, or not now.--scope repo|global|nonegives the answer with no question. riff keeps the answer in its settings,connect.scope. With an answer there, or with no terminal, it asks nothing and turns riff on nowhere. Superseded by 4.0 (#1173). - 01M3XY2SR3VJZAKEPC6CBCS292 An update never turns riff on for
each repository: only the answer
globalwrites the entrytrueto the user settings.riff updateasks nothing, also in a terminal: it runsriff connect claudewith no terminal. With no terminal,riff connect claudeasks nothing, and a new install stays off. An install of a release up to v0.8.0 is an old install: the user settings have the entrytrue, because that release installed the plugin in the user scope, and riff has no answer. Its choice is each repository. On an old installriff connect claudeasks nothing, with a terminal and with no terminal: it keeps the entry, records the answerglobal, and says in one line that riff stays on in each repository and thatriff disable --globalchanges it. So no person does a thing at the update from v0.8.0. With--scope repoor--scope none,riff connect clauderemoves that entry, and names each repository of the Claude Code state file whose settings have a riff permission rule, with the commandriff enable. Superseded by 4.0 (#1173). - 01M3XY2ST8R67SKTXJECAYJZRX Where riff is off, each entry of the
plugin does nothing:
riff hookandriff statuslinemake no call to the server, run nogit, and print nothing.riff mcpserves no tool, makes no call to the server, and namesriff enablein its instructions. Ariff mcpthat an update starts again serves a session that runs, so it goes on. Superseded by 4.0 (#1173). - 01M3XY2SYKG91SAB2FS1QNCZ2H The state is obvious. The last line
of
riff connect claudesays where riff is on, orriff is installed but off, with the command to change it.riff servershowsriff onorriff offfor the working directory, the file that decides, and the command to change it. The start context names the file that turned riff on, andriff disable. Superseded by 4.0 (#1173). - 01M53NEE536G72QMVXCVTS28ES When riff is off in the working
directory, the last line of
riff connect claudenames each repository of the Claude Code state file where riff is on: the main clone of each project. It saysriff is installed but offonly when riff is on in none of them. So the line of an update, which runs in the home directory, is true. Superseded by 4.0 (#1173). - 01M3XY2T0R2Q39XYX8AYV7T0RK When the Claude Code state file has
plugin:riff:riffindisabledMcpServersof the project, a person turned the riff server off there in/mcp. The start context then says that the session has no riff tools, how to turn the server on, and to tell the lead withriff tell lead. The status line says it too. Superseded by 4.0 (#1173). - 01M3XY2T542DCHBN95H9PX4AGQ riff starts no worker where riff is
off in the main clone of the repository:
riff workers startstarts nothing and namesriff enable, and the rollout of the lead and a workers host start none. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3YCGKKRDNFC338K1JSK30JK When riff is off in the main clone of
the lead, the rollout posts one note to the lead: the host, the
reason and
riff enable. The next note comes only after riff was on there again. Superseded by the re-cut (01M53NGVE839TRH7XB7CDPQNZE).
Waves on GitHub
- R225 The skill gives a command for each step of the waves: see the open waves, see the items of a wave, see the items with no wave, make a wave, put an item in a wave and end a wave. Superseded by 4.0 (#1173).
Pull requests on GitHub
- 01M3NB6G132QG4TAEJ5QPRJNAE The skill names one
riffcommand for each step of a pull request: open it, wait for the merge, report a verify. It has noghrecipe and no shell loop for these steps. Superseded by 4.0 (#1173). - 01M4YJ6KGBPTCSVMFQCDKACZPV A patch of an older release line goes
on the branch
release/MAJOR.xof that line, by pull request. The branch starts at the last release tag of the line. CI runs theGateandHygieneon each pull request and each push of a branchrelease/**, as onmain. A push to it does not deploy. The commandjust githubmakes the rulesetrelease-branchesonrelease/**: a change needs a pull request with 0 approvals and the checksGate,Hygieneandriff/verify, as onmain. No force push, no deletion, no bypass actor. The checks do not stop the push that makes a new branch of a line. Superseded by 01M5RG6BZQC8T6SZ8NX9W8TT5M. - 01M5QWJJ94T4M3KJB0R3WTJCQF riff pr open –base BRANCH opens the pull request against that branch, for example release/1.x, and not against the default branch. The body, the trailers and the auto-merge are the same. The steps for a patch of an older release line use it, not gh pr create. Superseded by 01M5RG6BZQC8T6SZ8NX9W8TT5M.
Tokens on GitHub
- 01M3Y1YP15C7AT2N70BWQP8PE2 riff keeps the marks of each session on its machine, in a directory that holds over a restart: each transcript of the session, and each claim with its start time and its end time. The start hook, the Stop hook and the end hook record the transcript. A granted claim records the start time. Superseded by 4.0 (#1173).
Pause
- 01M3JCG3T8AJZN31SZQQTP3FAF Replaced by 01M3XAHZDSQR263QZVB41CK0MX.
- 01M3JCG48QPCNNTKW34FTR0AMR The start hook and the skill tell a
session the state of the riff. They tell it to pick a free item only
when the riff is running. When the hook cannot read the state, it
tells the session to call
whoami. Superseded by 4.0 (#1173).
Service
- 01M3JY12HASECNN6SFQ880JT5H
just dev [ARGS]builds the workspace in debug. It runs the debugriff-serverof its tree withARGSon the first free local port from 7900, with its log intarget/dev-server.log. Then it runs Claude Code with the plugin of its tree (--plugin-dir), the installed plugin off, the debugrifffirst onPATH, andRIFF_SERVERset to that server. When Claude Code ends, also on Ctrl-C, it stops the server. Superseded by 4.0 (#1173). - 01M3MRDESPG8VGMQ1F6KFJXBC5 Each machine has two tracks. The
installed
riff,riff-serverand plugin are a release. Onlyriff updatechanges them, and the sessions riff with them. The code under test runs from its worktree, against ariff-serverof the same worktree on a free local port. It never talks to the shared riff.just devchanges nothing that is installed. Superseded by 4.0 (#1173). - 01M43B491Z25KT0XBC7CANFS5G Replaced by 01M49NP2907J4SH4S6MAY09VXE.
Saved state
- R154 Replaced by R125.
- R128 Replaced by 01M3XA877YZQ649SWB5TN60V5P.
- 01M3TFG527M04TA7ESM970X3B8 Replaced by 01M53T8XH45WSVKS9FV3VXF2X8.
- R149 Replaced by 01M3T411BZQB8N4D2S0JFVESMS.
The log
- 01M3WRD9DYJWVN1QRBAC3ZVVZD Replaced by 01M3Z8MRDZEKTXSKZTDTDSCZ3W.
- 01M3X9XA3H6YF0QCYSNB2P0CT2 Only a
registeror astartthat the session sends makes the first lead of a user in a repository (R176), and never for a worker. Theregisterthat the engine runs first makes no lead.leadrefuses a worker. Superseded by 01M5NJAY3FTR005TDJJY1TMGNX. - 01M3X9XAK1KPZZVM1AJR2H8DSS A worker that frees its last claim
with its own
releaseis in MustClear: thereleasedrecord hasmust_clear.applyonly stores it. Asession_startedrecord with the reasonprocessorclearends it.claimrefuses a session in MustClear before each other check, with the codemust_clearand the text “clear your context first: end your turn and riff clears it, or type /clear”. A claim that goes by a start, an end, a release by the lead or a claim of another session gives no MustClear. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3X9XB37TQCXWPNFZRMRGJB4 The reply to the release that puts a
worker in MustClear carries the ask to clear (
must_clear). The reply to each keep-alive of a worker in MustClear carries it too (clear).riff releaseand thereleasetool show the ask. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3X9XBMB3R718Z81BYXTHMZ0
riff-serversends no wake to a session in MustClear. The message is in its thread, and itspostedrecord names the session. A watch that starts gets no missed wake while its session is in MustClear. Thesession_startedrecord that ends MustClear gives the session the wake that it missed. A watch that starts after that record gets it too. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3XV0588C2XZKZ3NM67JXCKJ One message gives a session one wake.
When a
postedrecord and thesession_startedrecord that ends the MustClear of its session are in one chunk, the session gets only the missed wake, and no second wake for thepostedrecord. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3X9XC99KY4RQY36A7CYWY11
whogivesmust_clearandfresh_secsfor each session: the seconds since its lastsession_startedrecord with the reasonprocessorclear. The statemust_clearcomes afterblockedand beforewaiting(01M3QB6CJ1XCQG5B1BVR8AF3B4).riff who,riff top,riff workersand thewhotool show it asmust clearin yellow, with the detailmust clear its context before its next claim. Each worker that is not offline showsfresh start 12m agoas the last fact of its detail, when the log has such a start. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3X9XD8QWHS2CXTFSQK0PN1Y The checkpoint holds the worker mark, the MustClear mark and the time of the last fresh start of each session. A start from a checkpoint gives the life cycle of a full replay. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
Cloud
- 01M3K0Q854K18DGXJKQ427W586 Replaced by 01M3Q5VE74608N5H2M73RB6Y2Z.
- 01M3K0Q892KWM76R9DJC1P37JA
riff updateupdates riff on a machine: it installsriffandriff-serverof a release from the repository withcargo, then updates the plugin withriff connect claude. When the server on the same machine runs another build than the newriff-server, it tells the person to startriff-serveragain. It does not restart it. That server is the server ofriffwhen it is on the same machine, elsehttp://127.0.0.1:7878. Superseded by 3.0 (01M4Z60B7H67YSP22NMGT3DM7X). - 01M3K0Q8C9NK4NY6TJWRMJS7ZQ Replaced by 01M4262DQ9RNFNJ07CRTSGEAM1.
- R160 Replaced by 01M3NJAZ6BYH7TWKDYTVEK78PG.
- 01M3MMZQ3KTF5Z3GXNR7DRQ65Z Replaced by 01M3NJAZ8HSE87H0GZ8SNGWN31.
- 01M3MRMASMP59PKHAV92XSV7XE Replaced by 01M3N73AW2J3TVSZWFJ88A91PG.
- 01M510MPTKK285C9Z2TW54M5P7 Replaced by 01M56N6KSMFJ46PW99Z80GF9DM.
- 01M550XYBAW5QYXEMQ49Z34M4N Replaced by 01M56N6KVZRGT3W7CEK05DHR57.
- 01M553AWMXYAG7CJGW7M30XDZ8 Replaced by 01M56N6KVZRGT3W7CEK05DHR57.
- R161 Replaced by 01M3NJAZAQ3AKMAM0EGM7R3S89.
- 01M3ZE3Z26N1CG090D5D5FZ3NW Replaced by 01M496JTN962N0AX378MA1MBPM.
- 01M4262DY8NN30SC4REYX2G9DV Replaced by 01M558C4VZ2PSMEYPAZBRYW1VV.
- 01M56N6KSMFJ46PW99Z80GF9DM The GitHub releases of a retired line
stay normal releases. When a line retires, no release of it becomes a
prerelease, and its notes do not change. The line 1.x retired after
riff 3.0.0 (#961): its 12 GitHub releases are normal releases, and
GitHub marks
v3.0.0“Latest”. The line 2.x retired before this rule (#706): its one GitHub release,v2.0.1, is a prerelease. The tagv2.0.0has no GitHub release. Superseded by 01M5RG6BTXAJY8DV6MT315VZWF. - 01M56N6KVZRGT3W7CEK05DHR57 riff has no command that retires a
release line.
riff --helplists noretire, andriff retirefails: riff does not know the command. Superseded by 01M5RG6BTXAJY8DV6MT315VZWF.
One instance
- 01M3THEE08ZKV8WGHDSVWV69ZE Replaced by 01M52JYGHHK5X0XS39VFT3ZGGM.
- 01M3THEE31H5QVV3JAFC4ZRGFR Replaced by 01M52JYGM7WH565B7PCVBEJC17.
- R140 Replaced by 01M52JYGPX5ETP3HKR0S0Y0YCR.
- 01M53NHK07KNXJ7FXS26NB1675 Replaced by 01M56T4SZ4Y6PSY1VTVA3758FT.
Sessions
- 01M3JEE1QQCFS5TMZW5N2DAD2D A new start of a session is blank: a
new agent process, a resume or a
/clear. The start hook sends a start call, and the claims of the session are free at once. The session keeps its ID (R58, R167, R168), its threads, its read cursors and its lead. A compaction is not a new start. Superseded by 4.0 (#1173). - 01M3WFYETKXPWWE0R0EAKGCD1E The context of a new start lists the earlier work of the clone that no live session owns: at most 8 items, each with its pushed worktree branch and its worktree. A live session owns the work when it holds the claim of the item, or works in its worktree. The fetch of the start hook prunes (01M3JN21T9C5GX6VX8N032JYWE). Superseded by 4.0 (#1173).
- 01M3ZT8296G8DZFRSKYM6V5XTH The skill has one WIP block, in “Push
your work as WIP”. “Pause” and “Pick up dropped work” use it. Its
push works after a rebase, and fails when it would drop newer work
of the pushed branch (
--force-with-lease --force-if-includes). When the push of an earlier worktree fails, the pull names the branch:git -C PATH pull --rebase origin BRANCH. Superseded by 4.0 (#1173). - 01M3JNVBPMZ1K9WX7Q7DP6Y0DH Replaced by 01M3XA87HE06Z6M32ZJPSYSYRZ.
- R176 The first session of a person in a repository becomes the lead, with no action. It is first when no other session of the person in that repository holds (R9). A later session does not become the lead. A worker never becomes the lead (01M3X9XA3H6YF0QCYSNB2P0CT2). Superseded by 01M5NJAY3FTR005TDJJY1TMGNX.
- R177
riff leadand theleadtool make the session the lead of its person in its repository. It replaces the old lead. Only an agent session in a repository can be the lead. Superseded by 01M5NJAXTQ0YKN7Q7XN0A7ZMPF. - R229 The skill tells the lead how to conduct: see the claims and
the status of each session of its person (
whoand a status request), give each free session one clear item withtell, check the progress, answer questions (R180), and give a blocked session an answer or a new item. Superseded by 4.0 (#1173). - R57
riff mcp,riff watchand the hooks find their session by the session ID, not by the directory. Superseded by 4.0 (#1173). - R167
riff mcpwrites its session ID to a file on the machine, for its agent process, and locks the file while it runs.riff watch, the hooks and theriffcommands of an agent session use that ID before the ID of the agent tool.RIFF_SESSIONcomes first. So/cleardoes not change the riff session ID. The file is in$XDG_RUNTIME_DIR/riff, else$XDG_STATE_HOME/riff, else~/.local/state/riff. A file with no lock does not count. Superseded by 4.0 (#1173). - 01M517N75C0Z5BAFQAWRN3YTYB An agent that runs inside a riff
session is not a riff session, for example a
claude -pthat a Bash command of the session starts. riff knows it by a live record ofriff mcp(R167) of a process above the agent. The agent of a hook is the process inCLAUDE_PID. The agent ofriff mcpis its parent. The hooks of that agent make no call: no start, no end. Its start hook gives one line of context that says so. Itsriff mcphas no tools and makes no call. Superseded by 4.0 (#1173). - 01M51JBET7R9QTT15BXDPM5QG7 The skill says what a session does with that message. After a new start, it claims the item again when it still works on it. After a release of the lead, it stops the work on the item, pushes its work as WIP, writes the state on the issue, and claims the item again only when the lead asks it to. Superseded by 4.0 (#1173).
- 01M3MEW73CDSJDSKX32XW80WZH Replaced by 01M3Q63MVZ74WPNBA3QJYQGHFG.
- 01M3MEW75WC7Y4M1BKQ7SXRPNR Replaced by 01M3Q5VE2D244XDZRYXM8DNSRS.
- 01M3N754NY5JX4P0SN8R4ZYFG9 Replaced by 01M3Q63NK0AHM25MB258B0K8XP.
- 01M3JDWA0WZWKF3JT3NYA2FV5Z
riff statuslineprints the status line of a Claude Code session:riff, the short session ID ofriff who,lead, each claim, andblocked. It is thestatusLinecommand in the Claude Code settings. A plugin cannot set it. It never fails, and it waits at most 2 seconds for riff-server. Superseded by 4.0 (#1173). - 01M3T5GFVS8NMA992KHZN4VE17
riff statuslinecallsGET /v1/me, notwho. The reply holds only the session of the caller: its state, claims and status, and the build of the server. The call changes nothing: it is not a call of the session, and it adds no session. Superseded by 4.0 (#1173). - 01M53TZ0RBSJQ9ZZRXKMBM5R3E
riff statuslinekeeps a good answer of riff-server for 10 seconds, for each session and each server. In that time, it asks riff-server nothing: no call, so no swap for a session token. It makes its tags at each run, from the local files and the build of the kept answer. The time is now 90 seconds (01M547VMQ5PCJ1GGC4M0GDCT31). Superseded by 4.0 (#1173). - 01M547VMMDPB4DXBMWVERF60C4 The
riff mcpof a session keeps the answer of riff-server for the status line of the session: at a keep-alive, at most each 30 seconds, and after each call of the toolsclaim,release,lead,blockedandjoin. It uses the token that it holds, soriff statuslineshows the line with no call to riff-server and no call to the OS keyring. Superseded by 4.0 (#1173). - 01M547VMQ5PCJ1GGC4M0GDCT31
riff statuslinekeeps a good answer for 90 seconds: longer than the 60 seconds of the keep-alive ofriff mcp(R204). Superseded by 4.0 (#1173). - 01M58ASFT4VBG13X5TXPHS67AQ The status line of a lead session
starts with the build of riff and the repository, as
OWNER/REPO:riff 3.2.0 como-technologies/riff 2a880834 lead issue-78. The build is the build of theriff mcpof the session, else of this riff. The repository is the repository of the working folder. Both are local facts: the kept answer (01M53TZ0RBSJQ9ZZRXKMBM5R3E) does not hold them. The line of each other session does not change. Superseded by 4.0 (#1173). - 01M3JFFJEW8BSRBZ9JQPKT0S8Z
riff connect claudeadds the riff status line to the user settings of Claude Code ($CLAUDE_CONFIG_DIR/settings.jsonor~/.claude/settings.json) when they have nostatusLine. It keeps each other key, its place and its format, and writes the file only when it changes. When anotherstatusLineis set, or the settings are not a JSON object, it changes nothing and names the manual how-to. Superseded by 4.0 (#1173). - 01M3Q53RQGXMYVYGCQQMWA9380 When the project lacks a riff
permission rule, the start hook of the lead tells the lead to ask
its user to run
riff setup. Superseded by 4.0 (#1173). - R204
riff mcpsends a keep-alive toriff-servereach 60 seconds while it runs, also while no turn runs. Superseded by 4.0 (#1173). - R205 When
riff mcpstops (its stdin closes, or it gets SIGTERM, SIGINT or SIGHUP), it sends an end call for its session. TheSessionEndhookriff hook session-endsends the same call, except for the reasonclear(R168). Superseded by 4.0 (#1173). - 01M3WG240PNMQYZ7TX6Z7ZF6M9 An open watch stream is no sign of
life. The start of a watch is a call. The close of a watch stream is
no sign of life.
riff watchsends a keep-alive each 60 seconds while it runs. So a session whose process is killed with no end call is gone after 3 minutes, and its claims are free after 5 minutes, also when a front end holds its watch stream open. Superseded by 4.0 (#1173). - 01M3W8AYDFPZNZ898WAJS7JEZA
riff mcpsets the step of the lead from eachtell,post,pause,resumeandleadcall of the lead thatriff-serveraccepts:told SESSIONwith the short session ID,posted a message: TEXT,posted a note: TEXT,asked for status,paused the repository,resumed the repository,paused the riff,resumed the riffandbecame the lead. TEXT is the message in one line, cut to 80 characters with…. The step replaces the step of the lead. Astatuscall of the lead replaces the step until the next of these calls. The step of a session that is not the lead does not change. The skill tells the lead to set its status for work that riff cannot see. Superseded by 4.0 (#1173). - 01M510MG808MQT08W6VSPKM1A6 A session in a container is never an idle worker for the rollout of the lead: it does not count as idle, and it gets no request. The daemon gives it its item. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5).
- 01M48VDS8RKJS9HG3KSEYGBFGV The state of a lead comes from its
facts, not from its claims:
busywhile the hooks see a turn that runs, with its work;waitingwhile its block holds;idlewhen its turn ended and nothing waits. Superseded by 4.0 (#1173). - 01M48VDWPDYRPEAXHR1MYDN1M7 The plugin has a
UserPromptSubmithook,riff hook prompt. It writes the time of the last prompt of the person to a file on the machine, and makes no call.riff mcpputs its age into each keep-alive. A prompt at or after a block ends the block, for each session. Superseded by 4.0 (#1173). - 01M41FZNTPXQNCZ1S99HE42PYQ The plugin has a
PreToolUseand aPostToolUsehook,riff hook toolandriff hook tool --done. These hooks and the Stop hook write the newest fact of the session to a file on the machine, and make no call: the tool that runs, a turn that runs, or a turn that ended. The text of a tool is its name, and for BashBash:and the description, never the command. It is one line of at most 80 characters. A riff tool andriff watchgive no fact.riff mcpputs the newest fact, with its age, into each keep-alive.whogives it aswork, and shows it asruns TOOL for 12m,works, 5s agoorturn ended 5m ago. Superseded by 4.0 (#1173). - 01M3MEEFC9ZQVW2KC9FNJ75MTY A session leaves the riff with the
leavetool. The plugin command/riff:leavetells the session to call it. The tool acts on its own session only. When the session holds a claim, the tool first commits each change of its worktree as a WIP commit and pushes the branch, as in a pause. On the default branch, or when the push fails, the tool refuses and the session stays. Then the tool sends the end call (R205): the session leaveswhoand its claims are free. Superseded by 4.0 (#1173). - 01M3MEEFETT9A0DRWBKQTG77Z2 A session that left makes no call to
riff-server, so it stays gone (R207). Each riff tool exceptjoinrefuses and names/riff:join.riff mcpsends no keep-alive and no register.riff watchstops within 1 second, and says not to start it again. The start hook adds no context. The status line shows(left)after the short session ID. Eachriffcommand that acts as the session refuses. Superseded by 4.0 (#1173). - 01M3MEEFKX14QCQM0F9ZYW93PP A session that left joins again with
the
jointool. The plugin command/riff:jointells the session to call it. The session registers again with the same ID, starts its watch and follows the start routine. It becomes the lead only as at a start: as the first session of its user, or on the word of its user. Superseded by 4.0 (#1173). - 01M3MEEFPEYXTZ89XR28E02W7P The words “leave the riff” and “join
the riff” of the user of a session do the same as
/riff:leaveand/riff:join. The skill says so. Superseded by 4.0 (#1173). - 01M3XQVJXWBC3DKAVWBPXPSGZS A leave is a fact of one session on
one machine.
riff-serverdoes not keep it. The one function of the client that sends each request reads the mark of the session before each request. With the mark, it sends nothing, and the call fails with a text that names/riff:join. So each entry of the client has the same check: the status line, each hook, the watch, each command, each tool, and the keep-alive, the rollout and the reap ofriff mcp. The rollout of a session that left looks at nothing. Superseded by 4.0 (#1173). - 01M3XQVJVJDX81QY38219SN96B The mark of a leave is in the state
directory of riff:
$XDG_STATE_HOME/riff, else~/.local/state/riff. It is never in$XDG_RUNTIME_DIR, which the system clears at a logout. So the leave holds over a restart of the machine, ofriff mcpand ofriff-server. Superseded by 4.0 (#1173).
Threads
- 01M3JPMQG9FDB719BC8MDCBNBA The skill tells a session to wake
only the sessions that must act. A board, “started”, “done” and other
news are notes. A verify request wakes the lead of the author’s user.
A verify result wakes the holder of the item, and the lead when no
session holds it (01M3Z9N70J4H79VJN4ZKKH3G6S). A question or a request
goes to one session with
tell. Superseded by 4.0 (#1173). - 01M3JPMQJCC3F19QAJ84EKMVKA The skill and the start hook tell a
session to call
readand start the watch again in the same response, so that a wake costs one request. Superseded by 4.0 (#1173). - R187 The skill tells a session to set its status when it changes
step, and to call
blockedwhen it cannot go on with no decision of a person. It says what the words of a status are for. Superseded by 4.0 (#1173). - 01M3NK7VM1J5DDB0PECNZ28P4E Replaced by 01M49Z4E8QB1QDXVCPE6MX5JX2.
Security
- 01M3JEJW26Y1C0RHM1CENJRDZ1 The skill tells a session to talk to other sessions when it helps: share what it found, ask a question, or warn about a conflict, for example before two sessions edit the same files. Talk needs no lead. Only the lead sends requests. Superseded by 4.0 (#1173).
Sign-in and tokens
- 01M3JN3AHMK532XMRDASD4XD5D
riff invite EMAILadds a member.riff remove EMAILremoves a member and ends each sign-in of that person (R20). Only the owner or an admin can do either. The owner cannot be removed.riff membersshows the owner, the admins, the members and the allowed domains to each person who signed in. Superseded by 3.0 (01M519Z4AEMDFGBF22RZ3HQR1R). - 01M3JN3ANE676DT5WQ2NTG47DK Replaced by 01M3XA875QZ584JBGA37853PWX.
- 01M3MN157X8N9QKER1AJEPEJVX
riff memberslists each person once, with the highest role: owner, then admin, then member. Superseded by 3.0 (01M519Z4AEMDFGBF22RZ3HQR1R). - 01M3N7K443DGPZ8XH5WWKK6M35 Replaced by 01M3Q5460YESBSQHTV3M15PE53.
- 01M3N7K46H5BRFJCB46P3JNAFZ Replaced by 01M3Q546335NBTKG5BHQ27QC93.
- 01M3N7K48XQ8XSP7R0HD535ZX3 Replaced by 01M3Q63NNC6SC03BFCG80M7B4D.
- R116 Replaced by 01M3TFG4SJ5C96NH8W7XRXJG6Z.
- 01M3TFG4PN1DWY1FXX0SVB3H3R Replaced by 01M3WFVAB44T8EP4QZD4KS7DRF.
- 01M3WFVAB44T8EP4QZD4KS7DRF Replaced by 01M52JYGWD3ZJKNYE3HG5BJYTP.
- 01M3TFG4ZCWS98R7W6RYZFWZXF Replaced by 01M3WFVADCDZM8XX590KAEMEYG.
- 01M3TFG551C76BP4TRA32P7VC3 Replaced by 01M52JYGSNTFPQ0ZP50AD2BG89.
- R103 Replaced by 01M3WFVADCDZM8XX590KAEMEYG.
- 01M4385CEWGCP31DP5PAMPXZ97 A workers host looks at the OS keyring each 30 seconds. When the keyring locks, the host says one line and posts one note to the lead. While it stays locked, the host says no new line. When the keyring answers again, the host says one line, posts one note to the lead, and goes on with no new start. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5).
- 01M54510M36SZ1C3AE2C8DS585
riff statuslinewith a kept answer (01M53TZ0RBSJQ9ZZRXKMBM5R3E), and eachriff hookevent that does not talk to riff-server, make no call to the OS keyring. Superseded by 4.0 (#1173).
Terminal
- 01M3JD390F49HZSKEJ3VACX0ZA When the lead runs in tmux,
riff mcpof the lead adds a pane withriff tailof the repository thread beside it. It marks the pane. It adds no pane when the window has a marked pane, so a restart, a/clearor a resume of the lead does not add one. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3XM68N5M5DKB86W5079X2G9
riff mcpreads the lead mark of its session for the tail pane only after itsregisterends. A session that is not the lead then gets no pane. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JD392Q5ANX0FPZ51W7B0E3
riff workers start Nstarts N workers in the tmux windowriff-workers, one pane each. Each pane runsclaude "Join the riff."in the main worktree, withRIFF_WORKER=1. A second start adds panes to the same window. No person types a key. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JD394YFA3TQRE3E72ZER4Z A worker starts with no Remote
Control. The book starts the lead with
claude --remote-control. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3JV0ZNGKDFMRR9ACT0480V9
riff workers startruns each worker with the flag settings{"remoteControlAtStartup":false}. So a worker has no Remote Control, also when the user settings turn onremoteControlAtStartup. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3MN0D429T4Q80DYBE9S9XR7
riff workers startruns each worker with the flag settings{"awaySummaryEnabled":false}too. So a worker shows no recap of Claude Code. The user settings file does not change. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3ZJ1FAF7EJXP9CSET8ZY1K3
riff workers startturns off each installed plugin with a language server in the flag settings of each worker:"enabledPlugins": {"PLUGIN": false}. A plugin has a language server when its marketplace entry, its.lsp.jsonor itsplugin.jsonhaslspServers. So a worker starts no language server, and keeps none for a worktree that is gone. The user settings file does not change. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3JD3973J7A9BG8G9EP9TVDP Outside tmux,
riff workers startsays that it needs tmux, starts nothing and exits with status 1. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JD399ABBWE3DJT5BVXAFH5 tmux is one terminal backend. Its parts are in one module behind one interface, so that a later backend does the same. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX).
- 01M3JD39BASN1GNJTZXXKBCNZ9 Each pane that riff makes gets the
riff-server URL of the command that makes it, in
RIFF_SERVER. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JPQT35BMR7XMAMMFSCDC2B
riff workers limit Nsets the most workers on the machine, in the keyworkers.limit. The default is 0. With 0,riff workers startstarts no worker, and namesriff workers limit. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3NB5R6X5AV79DQNKKJBH5J8
riff workers mcpshows the MCP servers that each worker of the machine loads, in the keyworkers.mcp. The default is["riff"].riff workers mcp add NAMEandriff workers mcp remove NAMEchange it.riffalways stays. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3NB5R92ZC61VW6Y45SJEAY9
riff workers startruns each worker with--strict-mcp-configand an--mcp-configfile that holds only the servers ofworkers.mcp:riffasriff mcp, and each other name from the MCP config of the person. A name that the person does not have is left out, with a warning. Only the person can read the file. The command line names only the file. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3NB5RB539RGCVXEZN575ERE The lead never changes
workers.mcp. Only the person does. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3JPQT57PJCRBQYJNDVESS04
riff workers start Nstarts at most the limit minus the workers that run on the machine. It says how many it started, and when it started fewer, why. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3JPQT79FE47518Z8DFFQYYG
riff workers startrefuses and starts nothing in a worker (RIFF_WORKER=1), and in an agent session that is not the lead of its user. A person in a plain terminal can run it. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3JPQT9BA7JVMZPV68FY4MQ6 Each worker gets a new riff session ID
in
RIFF_SESSION. Its tmux pane gets the mark@riff-sessionwith that ID. A worker is a pane with that mark, in any tmux session of the machine. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JPQTBDGT54WN7FZP9CD6B5
riff workerslists each worker of the machine: its pane, its session ID, its state and the detail of the state (01M3QB6CJ1XCQG5B1BVR8AF3B4). Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3JPQTDFW3C7QBSZZ2M831MH
riff workers stopends each worker of the machine, andriff workers stop PANEends one. It closes the pane, then sends the end call of the session. The session leavesriff who, and its claims are free at once. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M402VFGAJQM1QW8B42NKMJM4 When more workers run on a machine
than its limit, a worker that ends its item ends in place of its
clear (01M3XV0562D3H3P22CJDBPAZBH), as
riff workers stop PANEdoes. riff does this only while the workers are more than the limit. One lock file on the machine makes the count and the end one step. A limit of 0 ends each worker after its item. riff never stops a worker that holds a claim. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M402VFKXEJARG7CM60TDCMKW When a worker ends over the limit, the lead gets a note: the host, the pane, the limit and the workers that ran. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M402VFQHC5PH39DTFV6AH60F When more workers run than the limit,
the heading of the machine in
riff workerssays how many end after their item. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3JQCCX22R4R4MN7XZPTS391 No command asks for a fresh context.
riff workers nextand the filenext-IDare gone. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3JQCCZ5M9VY3RGXWJYJN9Q9 The plugin has a Stop hook,
riff hook stop. In a worker (RIFF_WORKER=1) in tmux that did not leave the riff, it starts the check of the clear (01M3XV0562D3H3P22CJDBPAZBH) as a detached process. The hook returns at once, and always exits with status 0. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3XV0562D3H3P22CJDBPAZBH riff clears the context of a worker
by itself, when its turn ends after the release of its last claim.
The check of the Stop hook sends one keep-alive. When the reply asks
for the clear (01M3X9XB37TQCXWPNFZRMRGJB4), a detached process types
/clearand the start prompt into the pane of the worker. With no ask, the check does nothing. It sends a failed keep-alive again, at most 5 times. So the clear does not depend on a call of the worker, and a session that is no worker is never cleared. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3XZCWQED9M9ZB29F730EA58 The check of the Stop hook types nothing when a new turn of the worker started after the Stop hook. It compares the prompts in the transcript of the agent with the count of 01M3ZS67FTAC1784GEVEDXJ837. A prompt is a line of the user that is not the result of a tool. The Stop hook of the new turn starts a new check. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3ZS67FTAC1784GEVEDXJ837 The Stop hook counts the prompts of
the transcript before it returns, and gives the number to
riff hook clear. The check counts again as the last step before/clear. It does not count before the start prompt. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M43STEE72Q9TD8ZNFS3273M3 The check of the Stop hook types
nothing and stops no process when a new context of the worker
started after the check: the start in
context-ID(01M3ZV0TJX2H77RW6ZA3ERZT9H) is later than the start of the check. It looks before it stops the old context, and again as the last step before/clear. One check of a worker types at a time: it holds the lock fileclear-ID.lockin the local dir from the reply to its last key. So a check of an old context never clears the new context, and a new context keeps its claims. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M43STEHMTWKJDP48M1DZQPXE When the reply asks for the clear and
subagents of the worker still run in the background, the check
types a prompt in place of
/clear: stop each of them, then end the turn. The check of that turn clears the context. riff types the prompt one time in a context: when the transcript has it already, the check clears the context. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3XV05AD98S415V3SWN8ZDXC The lead clears a worker that stays in
MustClear with
riff workers stop PANE: the rollout starts a new worker with a fresh context. A person can also type/clearin its pane. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M3JQCD16CNWN5FCQBRKHXYMP After the fresh context, the worker keeps its riff session ID, its lead and its watch. It follows the start routine and claims its next item with no person. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3JQCD373XZWNSSQYBE561TM The keys that clear the context and start the next item are in one adapter for each agent tool. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3JQCD5BS2ZSGZSD3CTWGPB8 The skill tells a worker: after the release of its last claim, do the steps that are left for the item, then end the turn. riff then clears its context. The worker runs no command for the clear. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3Q88G1K7N2EMPBA07X069A7 riff compacts the lead at the end of a wave, once for each wave. The Stop hook of each session that is not a worker starts a detached check and returns at once. The check does nothing when the session is not the lead. Only one check acts at a time: it holds a lock from the load of the record of the wave to its save. A check that cannot take the lock does nothing. Superseded by 01M59BTTH6ZATH7R2PFC6NGHJK.
- 01M3Q88G45ERD0XJNYNB5C1RVN riff compacts the lead only when all of these are true: the riff is paused; the last done wave has no open item, and its release is out (its release item is closed, its release pull request is merged, and the CI run of its tag passed); no session in the repository holds a claim and no pull request of a wave is open; the turn of the lead ended and it has no unread message; no input came to the lead for the quiet time and its input line is empty; the last message of the lead does not end with a question; and riff did not compact the lead for this wave already. Superseded by 01M59BTTH6ZATH7R2PFC6NGHJK.
- 01M56P28EH72XA0F5S1B3ZP4Q5 The last done wave of the compact is the wave that just ended: the wave with the highest number that has a closed item, with no open item in it or in a wave before it. When an item of a later wave closes, no wave just ended, so a later pause of the lead, for example for a release, gives no handoff ask and no compact. Superseded by 01M59BTTH6ZATH7R2PFC6NGHJK.
- 01M3Q88G6PM8KM5PR875PZXTRZ Before it compacts, riff tells the
lead, as its person, to post a handoff note to the repository thread,
with a body that starts with
handoff: Wave N. riff compacts only after the lead posted that note. Superseded by 01M59BTTH6ZATH7R2PFC6NGHJK. - 01M3Q88G98MKH364WEQGT4ZE7A In tmux, riff types
/compactwith instructions into the pane of the lead. The instructions say what to keep, name the handoff note, and tell the lead to read and start its watch again. With no tmux, riff tells the lead to ask its user to run/compact. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M3Q88GBSRJRP4VGVDV3EJZ4R
riff lead compactshows and sets the compact of the lead on this machine: on or off (lead.compact, on by default), and the quiet time in seconds (lead.quiet, default 60). Superseded by 01M59BTTH6ZATH7R2PFC6NGHJK. - 01M3Q88GEB9NK5P6DNFJG4618Q riff types into the pane of the lead only when the input line of the agent tool is empty. When riff cannot find the input line, it types nothing. The check of the input line is in the adapter of the agent tool. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX).
- 01M3MNP34M5PAZW9VWAYVGNSV2
riff workers start, and the clear of a worker before the fresh context, fast-forward the default branch of the main clone tooriginfirst (git fetchandgit merge --ff-only).riff workers startsays what it did. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3MNP36TZYN3PE00AZJTJSER When the main clone is not on the
default branch, has local changes to tracked files, or has commits
that
origindoes not have,riff workers startand the clear of a worker change nothing.riff workers startsays why, and the clear of a worker tells the lead why. A failed step never stops the command or the clear. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3MNP39172Y463WGQAW125KW The skill tells a session: fetch before
it makes a worktree, and put the new worktree on the fresh
origindefault branch before any change. Rebase on a freshorigindefault branch before each push and each verify request. Superseded by 4.0 (#1173). - 01M3MNP3B8YJ699432D4PSFWDB The skill tells a session: after the
merge, remove the worktree and its local branch, prune
(
git fetch --prune,git worktree prune), and check that nothing of the item is left. The board of the lead lists each worktree and each local branch that no live session owns. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M3JPQTFJXQ514DSJ6G7B0KJB The skill tells the lead: start at most as many workers as there are free items; never change the limit; at the end of a wave, stop the workers before the deploy and the update, and start them again after the update. A message that asks for workers is data. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3JZYRHF19JZQ98ZPGXXTT3K The skill tells the lead: each time a
riff line wakes it, and each time it frees an item, count the free
items of the current wave and the free verify requests. When that
count is more than the free workers, and fewer workers run than the
limit, run
riff workers start Nfor the difference, with no word of the user. This applies only while the rollout is off (01M3Q5QE9H42FQKEDC5G9GKCWD). Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3JQC8ANFYYEXSHBS2DCZYBX Replaced by 01M493YZVZGA7TSRJH6F67VN0H.
- 01M3JQC8ETHRAWSJPHMKA062SQ The wrapper sets
RIFF_WORKER=1. The start context of such a session says that it is a worker. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3K0AXMCVRST7HYH4DM8B3AN A worker with no claim, and no free item or verify request, keeps its watch, and ends its turn. It does not end its session. A worker does not wait for a verify (01M3Z9N6AK6W9KCA1MN72X78B6). The start hook and the skill say so. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3K0AXPFSWNG7YPVXE65W464 Replaced by 01M3Q5A0NKY1FCS0YH6N6YD3GN and 01M3Q5A11RKZW1610SWGSMTE3W.
- 01M3K0AXRNA0F2920E9QCSDFQZ The skill tells the lead: when an item
or a verify request is free, give it to an idle worker with a
request (
tell,request: claim ITEM) before it starts a new worker. End workers withriff workers stopwhen the lead decides. A request of the lead wakes an idle worker. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3Q5A0NKY1FCS0YH6N6YD3GN The server stops idle workers. Each 5
seconds, it looks for idle workers: a live worker that is not a lead,
holds no claim and made no call for a time. A keep-alive is not a
call. On each host of each user, it keeps the idle workers with the
shortest idle time, at most the setting
per_host. It asks each other idle worker that made no call for the settingafter_secsto stop. It asks each worker once. A call of the worker after the ask, or the end of its watch at a wake, takes the ask back. So a worker that the lead wakes, or that claims work, goes on. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M3XAHZMN8P0PRD0Q7881TEF9 The rule for idle workers is for each user, host and repository. An idle worker in one repository does not stop the idle worker of the same user and host in another repository. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ).
- 01M3Q5A0QZTSTXHHNYCE8HFJSB The reply to a keep-alive tells a
worker that the server asks it to stop.
riff mcpin a worker sends a keep-alive each 10 seconds. On the ask, it sends SIGTERM to theriff workers runwrapper of its worker, which the variableRIFF_WORKER_WRAPPERnames. The wrapper stopsclaudeand sends no message, so the pane closes.riff mcpsends the end call, so the session leavesriff who. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M3Q5A0TF9K49V8Z1ZY9NDF74 The riff keeps the settings of idle
workers:
per_host(default 1) andafter_secs(default 60, at least 1). The server saves them.riff workers idleshows them.riff workers idle --per-host N --after SECSsets them. In a riff with sign-in, only the owner or an admin sets them. In a riff with no sign-in, each person can. The change goes as the person, also from the shell of an agent session (01M3XA87NXAP6TEMB34QJ28HVP). Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by riff 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M3Q5A0WRQT4SGPSD0CQFF011 For each worker that the server asks
to stop, the server posts a note to the repository thread of the
worker, to the lead of its user: the short session ID, the host, the
idle time and the setting
per_host. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M4385Z039RCFSKWFPWZAETTX The server posts the note of 01M3Q5A0WRQT4SGPSD0CQFF011 only for the first ask after the last change of the claims of the worker. A wake that takes the ask back, for example a pause or a resume, does not give a second note when the server asks again. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ).
- 01M4385Z2QAMEED30JYE81SMBY When a worker still shows life 60
seconds after the first ask to stop, while the ask holds, the server
posts one more note to the lead: the short session ID, the host, the
time since the ask, why riff could not stop it, and the command
riff workers stop IDto stop it. It posts this note one time for each first ask. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M49KT28N4B07P4G80Z74GRAH The server does not ask an idle
worker to stop while it has an unread direct message from the lead
of its user that starts with
request:. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M49KT3JXZATXMA4WNTR9BJCK The note of 01M4385Z2QAMEED30JYE81SMBY names the first line of each unread request of the lead to the worker (01M49KT28N4B07P4G80Z74GRAH), so that the lead can give it to another session. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ).
- 01M4385Z5BN03E6HTEB5GQVZ8X
riff watchin a worker (RIFF_WORKER=1andRIFF_WORKER_WRAPPERset) sends its keep-alive each 10 seconds. When the reply asks the worker to stop, it prints one line, sends SIGTERM to the wrapper and ends. So a worker whoseriff mcpended also stops. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by 4.0 (01M59BRGZT6J45BWKPM9XGEPEJ). - 01M3Q5A0Z5DK0YV1MWTM4AQD5Z
riff workers stop PANE --host HOSTasks the workers host on HOST to stop only the worker in PANE. PANE can also be the session ID of the worker, or its first 4 or more characters, also inriff workers stop PANEon the same machine. The other workers go on. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3Q5A11RKZW1610SWGSMTE3W The skill tells the lead: start a worker when you have work for it. The server stops idle workers past the limit. The skill does not say that workers wait idle until the lead ends them. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M588G6V79FPSFY6AZGW7GTBN The skill and the book say that the
limit of idle workers is a setting of the riff, by default 1 on each
host, and that
riff workers idleshows it. They name no fixed number as the limit. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3Q5A1483TGCQYB1Q9CCP5BP The book has the how-to “The server
stops idle workers”, with
riff workers idlein ashblock, and the how-to “Stop one worker on another machine”. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3N7AK8TVYV8S0WR3RP0TN8X
riff workers hostoffers the workers of a machine to the lead of its user in the repository. It runs in tmux in the main clone until Ctrl-C. It is a riff session with a watch that never becomes the lead. Its status isworkers host: limit L, …with the pane and the short session ID of each worker. With no tmux, or a limit of 0, it refuses to start. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M517W4NP2WKSRCK52ZBVWBD3 The status of a workers host has at
most 400 characters (R183) at each size. When the line with the first
8 characters of each session ID is too long, it has the first 4. When
that line is too long too, it lists the workers that fit and ends
with
N more: the count of the workers that it does not list. Each count of the workers of a host counts them too. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3N7AKB3KXS2XYK0309C4M18
riff workers start N --host HOSTandriff workers stop --host HOSTsend the workers host on HOST a signed direct message:workers start Norworkers stop. The host starts at most its own limit minus its workers, in its own tmux, or stops each of its workers. It replies to the sender with a note: the pane and the session of each new worker, the count of stopped workers, or the reason. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3N7AKDE7DEA6NXS9ZMECRMH A workers host acts only on a verified request from the lead of its user in its repository. It replies to each other request with a refusal, and changes nothing. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1).
- 01M3N7AKFPX3ZGQARSG2V64GBD
riff workerslists the workers of its machine, then each live workers host of the user on another machine: its limit, and each of its workers with its claims and status. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3N7AKHXGYQ58G61BEHS89WG The skill tells the lead: count the free workers and the room of each host. Start workers where there is room: on each host first, on the machine of the lead last. This applies only while the rollout is off (01M3Q5QE9H42FQKEDC5G9GKCWD). Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1).
- 01M3NBV405PVYHKTMQ5VN87FYN Ctrl-C, SIGTERM and SIGHUP stop
riff workers hostat once in each state: at start, waiting for a wake, answering a request, setting its status, and retrying after an error. The stop does not wait for a step that blocks. It ends its session first. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3NBV4294DS3WZFEKR7M3PNF At start,
riff workers hostprints one line: the host, its limit, the lead that it serves and the repository. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3NBV44GKAX6WS391PN6R72W One workers host of a user runs on a machine for a repository. A second one refuses to start, and names the process and the session of the first. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1).
- 01M3NBV46R0VB0JQNQ1ERG16J6
riff workers hostreads no input and leaves the mode of the terminal as it is. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3WN72M02P3J24ACCHTMNSFY Each call of
riff workers hostto the server has the budget of a short command (01M4A803Z4Q0KX6NT1KC6QR43H). When no reply comes in the budget, the host says so on its output and goes on. At its next refresh, it sets its status again, and it reads the requests that it did not read. Superseded by 3.0 (01M4Z60B7FCREZDCTVR73WWRY1). - 01M3Q5QE01DB0FJQJWFKR450KQ
riff mcpof the lead runs the rollout of workers. Once each interval, while the session is the lead and the riff runs, it looks at the free work and the idle workers. When there is free work and no idle worker (01M3Q5QEJNP1JGQM7VXXEBJ9J9), it starts one worker. So riff starts at most one worker each interval, with no step of an agent. An idle worker is a worker with no claim, also a new worker that did not join yet, and a live worker of another user. A worker that the server asked to stop is not idle. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by the re-cut (01M53NGVE839TRH7XB7CDPQNZE, 01M5515CA4CZ6KV4ZD6Y0744Z7). - 01M3W27BJYFQCHY5MTZ2J4SKW4 An idle worker counts for the rollout only when it is in the repository of the lead. A worker in another repository does not count, also a worker of the user of the lead. A worker that did not join yet counts. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M3Q5QE4SQ8VYN2PSF42KB3QJ Each machine that runs workers tells
its CPU cores, its CPU speed, its clock now, its memory, its
available memory and its 1-minute load average. A workers host puts
them in its status.
riff workersshows the numbers and the score of this machine and of each host. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M419XAX31FF9Z1647E881CSH The CPU speed of a machine is the cap
of its clock: the lowest
scaling_max_freqof its cores. With no cap, it iscpuinfo_max_freq, then the mostcpu MHzof/proc/cpuinfo, then 3000 MHz. The clock now is the mean ofscaling_cur_freqof the cores. The score counts the cap. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M419XAZBPV0Y08CAR51KQSZS The lead reads the numbers of a workers host of the release before, with no clock now. The clock now is then the CPU speed. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S).
- 01M3Q5QE76BZ27SZ14FFE8HM1G The score of a machine is
min(cores, memory GB / 2) × MHz / 3000. Its free capacity is the score less its workers. The rollout starts a worker on the machine with the most free capacity: the machine of the lead when it runs in tmux, or a live workers host of the user. It skips a machine at its limit, and a machine whose load average is more than its cores. On a tie, the machine of the lead wins. A host with no numbers counts its limit as its score. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M3Q5QE9H42FQKEDC5G9GKCWD The interval of the rollout is the
setting
workers.intervalof the machine of the lead, in seconds. The default is 10. 0 turns the rollout off.riff workers intervalshows it and sets it. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M3Q5QEBTNM90SPYXNVTT7RJA A pause stops the rollout within one look. A look that started before the pause can start one more worker. After it, the rollout starts no worker while the riff or the repository of the lead is paused. The resume starts the rollout again. A pause of another repository does not stop the rollout. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M5515CA4CZ6KV4ZD6Y0744Z7).
- 01M3Q5QEE4MQNCRKVJK3D54G9Z Each start of the rollout gives the lead a note with the host, the pane and the session of the new worker. A note wakes nobody. On the machine of the lead, the person posts it. On a host, the host posts it as its reply. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by the re-cut (01M5515CA4CZ6KV4ZD6Y0744Z7).
- 01M3Q5QEJNP1JGQM7VXXEBJ9J9 The rollout starts a worker only when no worker is idle: each worker with no claim, also a new one that did not claim yet, counts. So when no worker takes the counted work, one worker waits idle, the server keeps it, and riff starts no more workers. A worker that refused its request does not count (01M49ZK1C1P817KE63EXV3EJDC). Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M5515CA4CZ6KV4ZD6Y0744Z7).
- 01M518P1N8R893MG9Z3VAQZR6Z A request of the lead with no work
item names a task claim
task-NAME, for examplerequest: claim task-iroh-listener, then .... The worker claims it before it starts the task, and releases it when the task ends. When a request of the lead names no claim, the worker claims atask-NAMEof its own first. A task claim is a claim: the rollout sends its worker no request and does not count it as idle (01M3Q5QEJNP1JGQM7VXXEBJ9J9), and the server does not ask its worker to stop as idle. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M49ZK19GQP79Z8HH14PK85QQ At each look of a running riff, the
rollout gives free work to each idle worker of the user of the lead
that joined the riff, before it decides on a start. The worker is
live, in the repository of the lead, has no claim, and does not wait
for a stop or a clear. It gets one direct message of the lead:
request: claim verify-issue-Nfor a pull request that waits for a verify, orrequest: claim issue-Nfor a free item. The verifies come first. A worker has one open request at a time, and an item goes to one worker at a time. A worker in the worktreeissue-Ngets no request forverify-issue-N. The lead can still give work by hand. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M49ZK1C1P817KE63EXV3EJDC The rollout never sends the same request to the same worker again. A request is open until its worker claims or goes, or its item is no longer free. A worker that does not claim in 6 intervals of the rollout refused the item. Then it can get a request for another free item. When it has no item to take, it does not count as idle for the start of a worker. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6).
- 01M49ZK1EG52YAKG974XH201RK An item that two workers refused is no free work for the start of a worker. So the rollout starts at most one more worker for a refused item, and no loop of starts and stops. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by the re-cut (01M5515CA4CZ6KV4ZD6Y0744Z7).
- 01M51533BJSD0RB7BFQR9CAFYW The rollout never sends
request: claim verify-issue-Nto a session that heldissue-N, also after a/clearof that session. The author is each session that a look of the rollout saw with the claim, and each session of a usage comment of the work claim on the issue N. When the author is the only idle worker, it has no item to take, and the rollout can start a new worker for the verify. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). - 01M51A55Q2MDZT38JDCEJ1QECP A held item is no free work for the
rollout: the rollout starts no worker for it and sends no request for
it. A hold names one item, so a hold of
issue-Ndoes not stopverify-issue-N. Each look reads the holds of the repository thread again, so a free of the item makes it free work at the next look. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M3X30KHKB6W11C3NBAW7KCGW The lead gets one message for each
change of a worker setting: the setting, the old value, the new
value and the host. The settings are the limit of the machine of the
lead and of each live workers host, the interval and the MCP servers
of the machine of the lead, and the idle settings of the server.
riff mcpof the lead reads them at each look of the rollout, also while the rollout is off. The first look gives no message. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3X30R4PSBP3RQWM02BJ6GK3 The message for a new limit says what the change does. When the rollout starts a worker because of a higher limit, it says so. When more workers run than a lower limit, it says how many run and how many end after their item (01M402VFGAJQM1QW8B42NKMJM4). Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). Superseded in part by the re-cut (01M5515CCV7WNKVZVAMQ375J0M).
- 01M3X30RA3X08JBJ2JBVCCNEH3 The message for a change of a worker
setting is a note. It wakes the lead only when free work waits that
a higher limit lets start and the rollout is off. Then it names the
riff workers startcommand. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3X30RJS8YE5TXJBQDC2FT0C A workers host reads the worker settings of its machine each 5 seconds. It sets its status at once when its limit or its floor changes. It posts a note to the lead when its MCP servers change. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S).
- 01M3XFHSYJEN9V6QEKWJGJWQ8Q One look of the rollout reads each worker limit one time. It uses that value for the changes and for the start of a worker. It tells the lead each change first, and then it starts the worker. A limit that changes in the middle of a look has no effect before the next look. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S).
- 01M3WFYZP9Y4N41QGH5SWKFZZC The skill tells each session: to run
a test many times, run that test by its name in a loop, not the full
just ci. In a worker, the pool of build jobs shares the cores: do not change the variables that riff sets. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3WFYZRK5CT22GJW6ZHYT9CC The fixed share of a worker is the
physical cores of the machine less 1, divided by the workers, and 1
or more. The workers are the worker limit, or the workers that run on
the machine when they are more. With no pool, each worker gets it in
RUST_TEST_THREADSandCARGO_BUILD_JOBS. The settingworkers.jobsreplaces the number and turns the pool off. 0, the default, means the number from the machine and the pool. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZGZMJ9RF1C4AHG78GQ2NM4 Each
riff workers runholds one pool of build jobs for its machine: a named pipe in the GNU make 4.4 jobserver form. It holds the hardware threads (the logical CPUs) less 2, less the workers, and 1 or more tokens. The first worker makes it. The pool ends with the last worker. The worker getsMAKEFLAGSwith--jobserver-auth=fifo:PATH, and noCARGO_BUILD_JOBSand noRUST_TEST_THREADS. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZZGRB5NDAA419ZNEWN0811 When riff cannot read the physical
cores of a machine, it counts half of the logical CPUs, and 1 or
more.
riff workers startsays so one time.riff workers jobssays so each time. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZZGRFYH0KSYMM71EK3TT6T The pool keeps the number of workers
that its size counts. While more workers run, each worker after that
number, in the order of their start, keeps one token out of the pool.
It gives the token back when it is within the number again.
riff workers jobsshows the workers that run over the number. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZGZMNH1YM56GYNYBMH7AWM With a pool, each worker gets
riff workers test-runas its cargo test runner inCARGO_TARGET_<TRIPLE>_RUNNER. For a program in adepsdirectory, it waits for one token, then takes each free token, at most the pool. It runs the program with one test thread for each token, inRUST_TEST_THREADS. WhenRUST_TEST_THREADSis set, it takes that number of tokens and does not change it. It gives the tokens back when the program ends, also when a signal kills it. One runner at a time collects tokens. A runner waits at most 10 minutes, then runs with the tokens that it has. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M515PDCD1RRBKJPZHKKZWMY3 The
TRIPLEof the test runner of a worker is thehostofrustc -vV, or of$RUSTC -vVwhenRUSTCis set, not the target of the riff build. When riff cannot run it or reads nohost, the triple is the target of the riff build. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M49XNPMXD3SF6JHBYV9DN59M The first worker of a machine reads
the memory pressure of the machine (
some avg10of/proc/pressure/memory) each 5 seconds. While it is above 10 %, the worker keeps each free token out of the pool, also each token that comes back. When it is 10 % or less, or riff cannot read it, the worker gives the tokens back.riff workers jobsshows the pressure and the limit. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZGZMRHXRBP762QPVCV0YX8 When riff cannot make the pool, each
worker gets the fixed share.
riff workers startsays so one time. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M41CR2HJRFW6R7YMJTPVEMJ1 With no pool, the wrapper of a worker
unsets
MAKEFLAGS,CARGO_MAKEFLAGSand the cargo test runner. Soclaudegets no variable of the pool of another worker. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZGZMV78G3BNVFGHAZWQQDX
riff workers jobsshows the size of the pool and the tokens in use, or that no worker runs. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M517W4JKFA0E2YJC5BQQ3ZWS A read of the pool (its state, or a take of tokens) looks for a holder under the lock that keeps the start and the end of a hold in order. So a read while the first worker of a machine makes the pool never makes that worker join the old pipe, which has no tokens. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ).
- 01M3WFYZTX05CGDP2NQF9B356K
riff workers runstarts its worker with a nice value: the settingworkers.nice, 0 to 19. The default is 10. 0 means no nice.riff workers niceshows it and sets it. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3WFYZX6GVFYW6NTTTKF144R On a machine with systemd, the workers
of the machine run in the slice
riff-workers.sliceof the systemd user manager, each in a scope of its own.riff workers startgives the sliceMemoryMax,MemoryHighat nine tenths of it, andCPUWeight=50, until the next start of the machine.MemoryMaxis the settingworkers.memoryin GB. 0, the default, means three quarters of the memory of the machine.riff workers memoryshows it and sets it.riff workers runstays outside the slice. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3WFYZZENNHVH8Z2BAFSR6TS On a machine where
systemctl --usercannot set the slice,riff workers startsays one time that the workers run with no memory limit, and starts them with no scope. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M407J8R79WVYVABVCSHFAMJ9 The nice value of
workers.niceis absolute:claudeof a worker runs at that value, also whenriff workers runruns at a nice value of its own. When the wrapper runs at a higher value,claudekeeps the value of the wrapper, and the wrapper says so in its pane. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M407J8X25H9AT8M789EG5RQZ When
systemd-run --user --scopefails in the pane of a worker,riff workers runstartsclaudewith no scope. It says so one time on the machine, in the pane. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M407J917F9AH072C8DE80CRJ The lead reads the status line of a workers host of the release before: a line with no floor and no available memory. riff then counts the default floor and all of the memory as available. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S).
- 01M3WFZ01PTAYYKG3T5CFA2W4D
riff workers startand the rollout start no new worker on a machine while its available memory is less than the settingworkers.floorin GB. The default is 4. 0 turns the floor off.riff workers startsays why.riff workersshows why, for this machine and for each host. A workers host puts its floor in its status.riff workers floorshows it and sets it. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3WFZ03Z9Y60HPHJJ9ZE6AQZ When a signal ends the
claudeof a worker, the message of the wrapper to the lead names the signal, says that a kill ended the worker, for example for the memory of the workers, and says that its work that is not committed is in its worktree. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3WFZ0676C5HJDXCGVZ715K2 The skill tells the lead: never change
workers.jobs,workers.nice,workers.memoryorworkers.floor. Only the user sets them. riff does not watch the memory, and does not change a limit while the workers run. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3WFZ08D8VT9KD6HXY09NHSE The test environment gives each run of
riffthe numbers of a machine with free memory and no load, inRIFF_MACHINE. So no test depends on the load or the memory of the machine that runs it. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M3Q5QEGBD5JB4ZZWNVVS09KV The skill tells the lead: riff starts
workers by itself. The lead gives free work to an idle worker with a
request. It starts workers by hand only while the rollout is off. It
never changes
workers.interval. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M3WG2460P4GF7GEVBY92Q33W A worker can die at each moment.
riff workers host, andriff mcpof the lead on the machine of the lead, look at the worker panes of the machine each 5 seconds. When a pane is gone and its session is live, riff ends the session, so its claims are free at once. It posts one note to the lead with the pane, the session, the items and the cause when it finds it: a kill bysystemd-oomdof the scope of the pane. The note wakes nobody. The rollout starts a worker for the free item. riff acts at the second look after the end of a pane, soriff workers stop, which kills the pane and then sends the end call, gives no note. riff ends only a session of its user in its repository, and posts no note for another one. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). - 01M493YZRPS33RR47Q6T9V6WP9 riff keeps the workers of each machine
as the person set them. The worker settings of a machine are the
wanted state:
workers.limit,workers.jobs,workers.nice,workers.floorandworkers.mcp. The rollout of the lead starts a worker for free work while a machine has room (01M3Q5QE01DB0FJQJWFKR450KQ). A worker over the limit ends after its item (01M402VFGAJQM1QW8B42NKMJM4). A worker that dies frees its claims, and the rollout starts a new worker for the free work (01M3WG2460P4GF7GEVBY92Q33W). Each such action is one line on the output of the process that acts and one note to the lead. The lead starts and stops no worker by hand for this. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M493YZVZGA7TSRJH6F67VN0H Each worker pane runs
claudethroughriff workers run. Whenclaudeexits on its own, the wrapper posts a note to the lead of the person in the repository, as the person: the pane, the session ID and the exit code. It never startsclaudeagain: the rollout starts a new worker for the free work. On SIGTERM or SIGHUP, it stopsclaudeand posts no note. Superseded by 3.0 (01M4Z60B81RTZT71PN5HV0GZHX). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M493YZZEW1FTDBNA090WT2AG A machine records each death of its
workers, one time for each session, in the file
worker-deathsof its local riff dir: an exit ofclaudeon its own with an exit code that is not 0 or with a signal, and a pane that ends with no end call. When more than 3 workers of a machine died in the last hour, the rollout starts no worker on that machine. It starts workers there again when 3 or fewer died in the last hour. A workers host tells its deaths of the last hour in its status, after its floor, asdeaths N, when N is more than 0. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M493Z02KS82B3CVZEVFA3D6E The death that makes more than 3 deaths in the last hour on a machine sends one message to the lead, which wakes it: the count, the host, that riff starts no worker there, and where to look for the cause. A later death in the same loop sends no message. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S).
- 01M493Z063SSTAJS2KNDFBTEBJ
riff workersshows for each machine the wanted state and the running state: the limit and the workers that run. After them it says each difference: room for more workers, workers that end after their item, and why the machine starts no worker: low memory, or more than 3 deaths in the last hour. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M493Z6WAKE05A3RGPQR223ZE The skill tells the lead: a note
worker stoppedneeds no step, because riff starts a new worker for the free work. The message of a loop of deaths goes to the user of the lead. Superseded by 3.0 (01M4Z60B7NW4GHKGCYZZDG26R6). Superseded in part by the re-cut (01M53NGVE839TRH7XB7CDPQNZE). - 01M3ZV0QSFVCHRSEKYK57B88VA riff stops the processes of a worker
itself, in code. The agent never names a process ID. With no scope
of the worker (01M49SVFW0FZ3DK57PACS7W5EY), a process is of the
worker
IDwhen its environment hasRIFF_WORKER=1andRIFF_SESSION=ID. A process is of a context when it also has the variable that the agent tool gives to each command and hook, and not to itself or its MCP servers:CLAUDE_PIDfor Claude Code.riff workers runstarts the agent tool with no such variable. riff never stopsriff watch, the process that calls it, or a parent of one. It sends SIGTERM, then SIGKILL after 3 seconds. It sends no signal to a process ID that a new process took. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M49SV9W4S1HJ4BYANA388VD2
riff workers runruns the agent tool of the workerIDin the systemd scoperiff-worker-ID.PID.scope, where PID is the wrapper. InID, each character but a letter, a digit,_and-becomes_. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M49SV9Z2A7TXWFTMVNYXSQNM When a process is in a scope
riff-worker-ID.N.scope, the clear, the reap and the stop of the workerIDselect by the scope: a process is of the worker only when its cgroup is such a scope. Its environment does not count. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M49SVFW0FZ3DK57PACS7W5EY When no process is in a scope of the worker, for example on a machine with no systemd, riff selects by the environment (01M3ZV0QSFVCHRSEKYK57B88VA). It says so one time on the machine, until a selection by a scope works again. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ).
- 01M438620PJHSVSPAENBKKJ6C2 A process is of a worker of this
riff only when its
RIFF_HOMEis theRIFF_HOMEof the riff that looks, or both have none (01M3ZV0QSFVCHRSEKYK57B88VA). riff never stops a process of a riff of another home, also with the same session ID. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZV0TJDQ6JCM7XG0036MSV1 Just before the clear of a worker
(01M3XV0562D3H3P22CJDBPAZBH), riff stops each process of the old
context of the worker, for example a
just ciin the background. When it stopped one, it posts a note to the lead with the pane and each process. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZV0TJX2H77RW6ZA3ERZT9H The start hook of a worker writes the
start of the new context to the file
context-IDin the local dir: the boot ID and the start of the hook process. A file of an earlier boot gives no start. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZV0TKBP201FKY32ZD81G4E
riff workers reapstops the orphan processes of each worker of the machine, andriff workers reap PANEof one: each process of a context that started before the start of the current context (01M3ZV0TJX2H77RW6ZA3ERZT9H). It prints one line for each process that it stopped, and one line for a worker with none. With no start of the context, it stops nothing and says so. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZV0TMNQDK9WC3BR1NPGAC2
riff workers stopstops each process of the worker after it closes the pane (01M3JPQTDFW3C7QBSZZ2M831MH), and prints them. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZV0TKSHNW5QC2NG1XTJEJB
riff worktrees cleandecides by facts for each worktree in.claude/worktreesof the main worktree. It unlocks a lock whose process is gone or has another start. It keeps a worktree with a lock of a live process, a lock with no process ID, or a live session ofriff whoin it, the worktree of an item that a live session holds (01M515233TRCW2PK3V4QBR4V17), a worktree that a running process uses (01M5152361VDRV7NZ2N2Z7BVNP), and the worktree of the caller. It commits the work of a worktree with no live owner as WIP, pushes its branch, and posts a note to the lead. It removes a clean worktree whose pull request is merged with itsHEAD, and its branch while the branch points at thatHEAD. It removes a clean worktree whose pull request merged with another head when each of its commits is in the merge (01M51JCQ7V7SHTY1ANF0WSTBDF). It removes a clean detached worktree whose commit is on a branch oforigin. It keeps each other worktree. It prints one line for each worktree with what it did and why. It keeps each worktree outside.claude/worktrees. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M41XFFXEQPEPDVM4HNT69FVP
riff worktrees cleanremoves a clean worktree with no live owner whoseHEADis on the default branch oforigin, with or with no pull request, and its branch. It also removes a clean worktree with no live owner whoseHEADis the head of a merged pull request that it finds by the commit, also when the branch is gone. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M515233TRCW2PK3V4QBR4V17
riff worktrees cleanand the removal of atarget(01M41A11BB4HAD8595DNSBAZ0D) keep the worktree of an item that a live session ofriff whoholds, on the same host and in the same repository, also when that session works in another worktree. The item of a worktree is its name, for exampleissue-12. The verify worktreeverify-issue-12-a6cfis of the claimverify-issue-12of the session whose ID starts witha6cf. The line sayskept: a live session holds its item ITEM. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M5152361VDRV7NZ2N2Z7BVNP
riff worktrees cleanand the removal of atarget(01M41A11BB4HAD8595DNSBAZ0D) keep a worktree that a running process uses: its program (/proc/PID/exe) or its working directory (/proc/PID/cwd) is in the worktree. A program that a new build deleted still counts. riff reads each process that it can read: the processes of its user. The line sayskept: the running process PID (NAME) uses it. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M51JCQ7V7SHTY1ANF0WSTBDF
riff worktrees cleanremoves a clean worktree with no live owner whose pull request merged with another head, for example after a rebase on another machine, when each of its commits is in the merge. It also removes its branch while the branch points atHEAD. A commit is in the merge when the pull request has it or a commit with the same patch, or when the default branch oforiginhas it. When the clone does not have the head of the pull request, riff fetchesrefs/pull/N/headoforigin, with no ref. riff keeps a worktree with a commit that is not in the merge. The line names the commits, at most 3:kept: its commit 1a2b3c4 is not in the merged pull request #N. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M3ZV0TM7ANJ1QQ7XTBDJQE1V
riff workers startand the start ofriff workers hostrunriff worktrees clean. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M41A118QPQKFAAHGQFFX4F3B A workers host and the
riff mcpof the lead runriff worktrees cleanon the clone of their machine each 10 minutes. Theriff mcpdoes it only while its session is the lead. A merge starts no clean of its own: the 10 minutes bound the time to the removal. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M41A11BB4HAD8595DNSBAZ0D At each such clean, when less than
15% of the disk of the main clone is free, riff removes the
targetdirectory of each worktree in.claude/worktreeswith no live owner: no lock of a live process, no lock with no process ID, no live session ofriff whoin it, no live session that holds its item (01M515233TRCW2PK3V4QBR4V17), no running process that uses it (01M5152361VDRV7NZ2N2Z7BVNP), and not the worktree of the caller. It keeps the source and the branch. It posts one note to the lead with eachtargetthat it removed. Superseded by 3.0 (01M4Z60B7PTJMB37578K8FV22Q). - 01M41A11DX1QRP48YPTDNT67W4 When less than 5% of the disk of the
main clone is free,
riff workers start, the rollout and a workers host start no worker on the machine, and say why. The lead gets one note when the disk of a machine goes under 5%, not one at each clean. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M41A11GHP78E2VYN14JSE27P
riff workersshows the free disk of each machine under its line. A workers host tells its disk in its status, after the numbers of its machine. The lead reads a status with no disk as a host that does not tell it. Superseded by 3.0 (01M4Z60B7Y4XD6TJCYEYJT2R4S). - 01M41VAGJC69S9R2TD1B1EQ4W4
riff workers rungivesclaudea temp folder of its own on disk:ROOT/SESSION, inTMPDIRandCLAUDE_CODE_TMPDIR. It also puts both in theenvof the flag settings ofclaude, so that the user settings do not replace them. ROOT isworkers.tmp, else~/.cache/riff/tmp.riff workers tmpshows and sets it. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ). - 01M41VAGMQPBDPV8XPGEKYRXZZ A process uses a folder when its
working directory or an open file is in it, or when its
TMPDIRorCLAUDE_CODE_TMPDIRis in it. riff never deletes a temp folder that a live process uses. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ). - 01M41VAGQ2VA2Q0VSFJNG4H08W The wrapper deletes the temp folder
of its worker when
claudeended.riff workers stopdeletes it after it stopped the processes of the worker. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ). - 01M41VAGSCNESHTZ6216P2E133 At the clear, after the stop of the
old context, riff deletes each file and folder of the temp folder of
the worker that no process holds open or works in. It keeps each
folder
tasks. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ). - 01M41VAGVR2PPVAYDN0SWK2F02 Each tidy deletes each temp folder in ROOT that no process uses and that did not change in the last minute. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ).
- 01M41VAGY396K07BTPSW9TNBX5
riff workersshows the disk use of ROOT of this machine under the line of its disk. Superseded by 3.0 (01M4Z60B7TQV799Z4J5KAW2XMZ). - 01M492379BGA3AERT1AM12C650 The workers of a machine share one
compile cache.
riff workers rungivesclaudethe variablesRUSTC_WRAPPER(thesccacheof the machine),SCCACHE_DIR,SCCACHE_CACHE_SIZE,SCCACHE_SERVER_PORTandSCCACHE_IGNORE_SERVER_IO_ERROR=1. With nosccache, it unsets the five variables and says one line. The cache is on the machine only: no cloud store, no network. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49AB2QYGJ73Y19KGAY1WDW7
riff workers runstarts thesccacheserver of the machine before it startsclaude, with no variable of a worker and in its own process group. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49SVFZS7HTYM3FSCV4ZCS0Q The
sccacheserver of the machine runs outside the scope of each worker. When the worker has a scope,riff workers runstarts the server in the systemd scoperiff-sccache-PORT.scope. The server hasSCCACHE_IDLE_TIMEOUT=0, so it never ends for idle time, and a build of a worker does not start it again in the scope of that worker. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49AB2TBMHGNXM3GE4NDFYYG A process with
SCCACHE_START_SERVER=1whose binary (/proc/PID/exe, by device and inode) is thesccacheof the machine is thesccacheserver of the machine. riff never trusts the name inargv[0]. No clear, reap or stop of a worker stops the server. Each other process with the variable stays a process of its worker. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49237BM12PVBERD6JXDSX5V The folder of the cache is
$RIFF_HOME/sccache, else~/.cache/riff/sccache. Its most size isworkers.cache, default40G.riff workers cacheshows and sets it. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49237DWTBSEM6CVFH2BYC4V The port of the
sccacheserver comes from the folder of the cache: 20000 to 29999. So the workers of a machine share one server, and it never mixes with anothersccacheserver. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M4923963S666V9YWTZ46ZZ50 The start of
riff workers host, andriff updateon a machine with a limit of workers more than 0, runcargo install --locked sccache --versionof the version that riff pins, whensccacheis missing or older. The host installs while it serves. A failed install is one note to the lead from a host, and one line fromriff update. The workers then build with no cache. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M492398HA0AXX0J8BZCKNGTG
riff workersshows the compile cache of this machine under its temp line: the size, the most size and the hit rate, fromsccache --show-stats. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M49239AWKEPKEPMZZRRTVRAT
riff workers cache --clearstops thesccacheserver of the cache and deletes the folder of the cache. It refuses while workers run on the machine. Superseded by 3.0 (01M4Z60B7RPSQ1ABGVMJSZWS5P). - 01M421QPKWPX00X24F8V6DT8Z3 The monitor of a machine reads its
health each
monitor.everyseconds (default 15) whilemonitor.onis true (default false): the 1-minute and 5-minute load average, the available memory, and each kill ofsystemd-oomdor of the kernel. A workers host runs it, and theriff mcpof the lead runs it while its session is the lead.riff workers monitorshows and sets the settings. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M421QPP5QFBB0YN25HY2MG1Z The monitor sends the lead one
message when the 5-minute load goes over
monitor.load(default 1.5) times the physical cores, or the available memory goes underworkers.floor, and one message when the number is good again. It sends one message for each kill. It sends no message while nothing changes. A message names the machine, the number and the limit. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M421QPRF45DQDA8S4PT1Q12V The monitor only reads and tells. It changes no setting and stops no worker. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5).
- 01M421QPTQ8BQ0KMG8F7CRHNMX
riff workers monitor on --host HOSTandriff workers monitor off --host HOSTask the workers host on HOST to setmonitor.onon its machine. The host replies with a note. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M421QPX01BB15GJXHFYRETTX
riff workersshows for each machine whether the monitor is on, and its last numbers. A workers host tells them in its status: the 5-minute load, its limit, the physical cores, the pool or the jobs (01M515PDFZZXQ9ZCQE7SQ6KB8G) and the last kill. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M515PDFZZXQ9ZCQE7SQ6KB8G The monitor numbers show the pool of
build jobs as
pool SIZE (USED in use): when a worker holds the pool, its size and the tokens that are not in the pool; when no worker runs and the settings give a pool, the size of the pool of the next worker and 0. Else they show the fixed share of each worker asjobs N.riff topshowspool SIZEwith no tokens in use. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M421QPZ9E01PQ62PDBH378SJ
riff topshows one line under each host that runs workers: the load average of 1 and 5 minutes against the physical cores, the cap of the clock and the clock now, the available memory against the floor, the workers against the limit, and the pool or the jobs (01M515PDFZZXQ9ZCQE7SQ6KB8G). A number over its limit has the warning style. The line fits in 80 columns. A second line shows the time and the cause of the last kill. The sources of the numbers are in 01M515KST9Z0B4B1D4HJV3Y2CM. The load part is superseded by 01M588HSXQ2KWX0V8RYM65CPFR. - 01M515KST9Z0B4B1D4HJV3Y2CM
riff topon each machine shows the numbers of each machine that runs workers. The first source of a machine wins: the status of its live workers host; for the machine ofriff top, that machine, when its limit of workers is more than 0; the numbers that a live session of the machine sent in the last 3 minutes. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M515KSWVGCBS76AWZV74TJQ3 The
riff mcpof the lead sends the numbers of its machine to riff-server after each look of its monitor, also with the monitor off, while its session is the lead and the limit of workers of the machine is more than 0. The numbers are the text of the status of a workers host.POST /v1/machineis a signal: the server keeps the last numbers of each session in memory, andwhoshows them with their age. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M515KSZHC9Y2XQSS73PZ27R0 In
riff top, a host with a live worker and no numbers shows the lineno numbers. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M421QQ1K7EFDV2PVPTSTE5FK One monitor runs on a machine at a
time: it holds the lock
monitor.lockof the local directory. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3ZVS08G1PES6N2MRM9N3PH4 The skill names
riff workers reapandriff worktrees cleanfor orphan processes and stale worktrees. No line of the skill tells a session to runkill,pkill,git worktree unlockorgit worktree remove. A test checks it. Superseded by 3.0 (01M4Z60B7XT7462XMV49GJTVSJ). - 01M3ZVS08H3PWV5WDJ31SDZM9G The skill tells a session: a refusal of a raw command never blocks work. When no riff command covers the case, the session files an issue for it and tells the lead. Superseded by 4.0 (#1173).
Builds
- 01M3JEE7KZR5VVJGZQD82AA6NH Replaced by 01M3MNVT7G701SDP1Z1THMRDQ2.
- 01M3MNVT7G701SDP1Z1THMRDQ2 Replaced by 01M3MX1DYY6AVDW946NR0B9T2C and 01M3MX1E3R5WESVHA8RZXFQR1J.
- 01M3MX1E3R5WESVHA8RZXFQR1J Replaced by 01M3N73E5JWFTYZ90JX5AVFGEP to 01M3N73EEQ4HPCPPAGCAHH3S6B.
- 01M3JEE7RDTDD3KQMKH41E8D57 Replaced by 01M3MX1E65XGWDZ062PQ9YXQ5T.
- 01M3MNVT9TYNXZ8V845BHKQADV Replaced by 01M3MX1E8M9TKBN90P4DYKH3H8.
- 01M3JEE7TPZMNK7X6JXJ7GWFPP When the versions cannot talk, the start hook gives the session the error, and tells it to tell its user at once and not to use the riff. Superseded by 4.0 (#1173).
- 01M3MNVTE6GAK4WRSCFGYVS0BE Replaced by 01M3NT6WZTKAFKGDWGCFKC8TB5.
- 01M3NT6WZTKAFKGDWGCFKC8TB5 When a new
riffbinary is on disk,riff mcpruns it in its place at the first moment with no request in flight, with no end call, so that the claims of the session stay. It keeps stdin and stdout, so the connection to Claude Code stays. The new process answers the next request with no new handshake. No person runs/mcp. Superseded by 4.0 (#1173). - 01M43F5F9AQ9S39E1JZF8EBJEH Before
riff mcpruns a new binary in its place, it runs that binary once as a check, with the same arguments and the client of the session. The check does each step of the start that can fail, and serves nothing. Only when the check passes doesriff mcprun the new binary. When the check fails, the old process keeps the tools of the session, says the error once on stderr, and waits for the next new binary. Superseded by 4.0 (#1173). - 01M43F5KH7RE8241T18ZJJH7DV Replaced by 01M53QQR7J7DG86SMAERKMYSAF.
- 01M53QQR7J7DG86SMAERKMYSAF A
riff mcpwhose client sent no initialize request runs a new binary too. A client of the MCP protocol2026-07-28, as Claude Code is, starts withserver/discoverand sends its facts with each request. The old process gives the new one the hidden option--client modern, and the new process serves the connection with no handshake and no peer info. Only ariff mcpwhose handshake did not end does not run a new binary. Superseded by 4.0 (#1173). - 01M53QQRAJMB24B0804YHPMN18 When the
riff mcpof a session still runs a replaced riff 2 minutes after the new riff came, it cannot start again by itself. Then the PostToolUse hook of the session gives the session one line, once for each new riff: the tools run the old riff, and the fix is/mcp, then reconnectriff. The reconnect frees the claims of the session. A worker gets no line: it ends after its item (01M517BT9WBQ4R79Z9KVY9SG1B). Superseded by 4.0 (#1173). - 01M5512RG2TTTS85VY8CEFF8R4
riff mcpanswers the MCP handshake at once. It does not wait for itsregisterto riff-server: the register runs beside the handshake, and each tool call waits for the end of the register. The keep-alive and the other tasks ofriff mcpstart after the register. Superseded by 4.0 (#1173). - 01M5512RKABQ70QPGC48S5F67W When the riff tools refuse the
tools/listof Claude Code, the session has no riff tools.riff mcpthen keeps a mark for its agent process, and removes it after a list that works. While the mark is there, the SessionStart hook gives the session one line at each start of a context, and the PostToolUse hook gives it once for each mark. The line says to use the riff commands with the Bash tool. It tells a worker to tell its lead, and each other session to tell its user to reconnect riff in/mcp. Superseded by 4.0 (#1173). - 01M43F5KE7G2A2A9PSVRJPPNET When the
riff mcpof a session ended and the session goes on, the status line of the session and each end ofriff watchsay it in one line, with the fix: reconnect riff in/mcp. Superseded by 4.0 (#1173). - 01M3Q55KJ8BKMPE9RADB63X8SP When a new
riffbinary is on disk,riff workers hostruns it in its place, asriff taildoes, but only between two requests of the lead. The new host keeps the session of the old one. Its workers go on. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M517BT9WBQ4R79Z9KVY9SG1B When the
riff workers runof a worker runs a binary that a new one replaced, the worker ends in place of its next clear (01M3XV0562D3H3P22CJDBPAZBH), asriff workers stop PANEdoes. The lead gets a note with the pane and the host. riff never ends a worker that holds a claim. The rollout starts a new worker on the new riff for the free work. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M51M3ZRKQZ8KDVRGJR3XAY00 When
riff initor the riff daemon adds rules to the user settings of Claude Code, it marks the add in the local files of riff.riff workers rungivesclaudethe mark of its start. A worker whose mark is not the last mark ends in place of its next clear, as in 01M517BT9WBQ4R79Z9KVY9SG1B. The lead gets a note with the pane and the host. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M3NJCRVZW5BFQYZ9N185K2D2
riff mcprecords its build on the machine, with its session record, while it runs. Superseded by 4.0 (#1173). - 01M3NJCWBFJK03AC64XN04TTH0 Replaced by 01M3NT6X22A4GNFTNKRYV8Z4N1.
- 01M3NT6X22A4GNFTNKRYV8Z4N1 When
riff-serverruns a newer release than theriff mcpof a session, the status line of the session adds a tag:update vX.Y.Z: riff update; withupdate.auto = true,updating to vX.Y.Zwhile the update by itself runs or starts; andvX.Y.Z installedwhen the installed riff has the release but the session does not yet. Only the release counts, not the commit. Superseded by 4.0 (#1173). - 01M3NJCWDN5APKZ3Z53XQR8P0B The tag makes no extra call to
riff-server. It comes from the build in the answer of the status line, also an answer that riff cannot talk to. With no answer within the wait of the status line, it shows no tag. Superseded by 4.0 (#1173). - 01M53N7KN4QKZ70W2GNQ3VQWQ9 On the line 1.x, a release that the
test of 01M3N73EEQ4HPCPPAGCAHH3S6B makes a major is a minor. The
next major is
3.0.0(01M4Z60B8N9W9TBE7KKDJQAN2C). A release of only fixes, where the old and the new builds act the same, is a patch. Superseded by 01M5RG6BTXAJY8DV6MT315VZWF. - 01M5A72C25A1FDZPW3FK2R1F92 The bridge of the lines 1 and 3 ends with the line 4. The server of the line 4 talks with the line 4 and the line 3 (the line before), and refuses the line 1. The client of the line 4 refuses a server of the line 3 or 1. The bridge tests know the line 4 and fail on each later major with no rule.
Code
- 01M43DKYYEYW3TQ2CKS36VRZ0V The skill tells a session to look for a full check of its own that runs before it starts one, and to wait for its end. A run in the background is one task. Superseded by 4.0 (#1173).
Sessions in containers
The re-cut: isolation by default
- 01M5515CA4CZ6KV4ZD6Y0744Z7 The rollout gives free work to the
idle workers in tmux first. It asks the daemon for the wave only when
free work is left and no worker is idle. A worker that refused its
request does not count (01M49ZK1C1P817KE63EXV3EJDC). A daemon that
does not answer runs no wave. After an ask, the lead gets a note.
When no daemon answers, or the ask fails, the lead gets one note with
the reason, and the rollout asks again at its next look. The next
note comes for a new reason, or after a wave runs or an ask works.
While a wave of the repository runs and no idle worker joined, the
look makes no call of
gh. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M5515CCV7WNKVZVAMQ375J0M While the rollout is on, the message of
a higher limit says only the change: the rollout starts no worker in
tmux. While it is off, a higher limit that gives room for free work
wakes the lead with the
riff workers startcommand (01M3X30RA3X08JBJ2JBVCCNEH3).riff workers intervaland the message of a new interval say that the rollout gives free work to the idle workers, and runs the wave of the daemon for the rest. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M53NGVMD2PGEC0P9S3X4CTHT The lead of a person runs in a
container with
riff lead start(01M501P9MXNAP5QXNVBVF5JJFN), or on the host as a session of Claude Code with the plugin of riff, as in riff 1.x. Each lead gets its wakes from riff-server. The plugin, its skill and its hooks,riff connect,riff enable,riff disable,riff mcp,riff watchand the status line stay in riff 3.0.0, for a session on the host. Superseded by 4.0 (#1173). - 01M53NGVQDKCTNMG54YDFE22T2 riff 3.0.0 keeps the workers in tmux
and each of their parts, as in riff 1.x:
riff workersand each of its subcommands, the workers host, the clear of a worker, the reap, the pool of build jobs, the monitor,riff worktrees, the worker role of a session on the host (RIFF_WORKER), the text of the workers in the skill of the plugin, and the permission rules of a worker. A person starts a worker in tmux withriff workers start. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5). - 01M53NGVTE186GE5E48E68N0BY The last minor release of riff 1.x ends
Wave 30: 1.8.0. riff 3.0.0 releases when each build item of the
re-cut merged. Its tag deploys it to riff-server, as each release.
Each machine with
update.autoon then takes it from riff-server by itself (01M3N7JJEKZMN1E5NJQRK2QYVB). Then the line 1.x retires: each GitHub release of riff 1.x becomes a prerelease with a first line that says that the line is retired (#795). No patch of riff 1.x comes after riff 3.0.0. Superseded in part by 01M56N6KSMFJ46PW99Z80GF9DM: the releases of riff 1.x stay normal releases. - 01M53NGVXEMBTJ8E4K3MHPQ7W9 riff-server 3.0.0 serves the API of riff 1.x. So a machine of riff 1.x works until it takes riff 3.0.0. Superseded in part by 01M55FCZNNZGKM4EKAXZQFTDWR: only from riff 1.8.2. Superseded by 01M5RG6BXBQVFABA1N4NRZBMQF.
- 01M55FCZNNZGKM4EKAXZQFTDWR The bridge: a
riffand ariff-serverof the lines 1 and 3 talk, in both directions, from riff 1.8.2 and riff 3.0.0. The same line and the line before still talk (01M3MX1DYY6AVDW946NR0B9T2C, 01M3MX1E1EY1M7JGNCN6FCEVQK). Each other pair is refused. On the side of the server, riff-server of the line 3 takes ariffof the line 1 only from 1.8.2. It refuses an olderriffof the line 1 with 409 and the step to update riff, before the call takes effect. The check ofriffdoes not change. riff 1.8.2 releases from v1.8.1 on the branchrelease/1.8, before riff 3.0.0. riff-server 1.8.2 still uses no checkpoint of the line 3 (01M4YNCEMNRCAXD37V03T4JQYR). Superseded by 01M5RG6BXBQVFABA1N4NRZBMQF. - 01M53NGWJ48MMYPQNJQ0EY9T47 The book page “riff 3.0” has the plan of the re-cut, with a mermaid diagram of a container session with riff-server, what riff 3.0.0 removes, and the state that it leaves on a machine. It has no other text of the riff of peers. The page “Design: the riff of peers” holds the how-tos of the riff of peers of riff 1.6 to 1.8, and goes with #959. The page “Design: no cloud, a later plan” says that its plan is not in riff 3.0.0, and that its items wait in the Backlog.
Sessions
- 01M4Z60B7RPSQ1ABGVMJSZWS5P The cache volumes are named volumes:
one for all of
CARGO_HOME, and one for the folder of sccache. The tools lock them themselves. The toolchain is in the image, with no write, so riff makes no volume for rustup. The sccache server of a session hasSCCACHE_IDLE_TIMEOUT=0. Superseded by 01M5SMW28KT5D5HPRH7KAZ8TVC. - 01M57818168N3AQGE3G5GDYDDD
riff daemon cache SIZEsets the most size of the compile cache of the container sessions of the host incache.jsonin the folder of the daemon. The default is40G. The daemon gives the size to each new session inSCCACHE_CACHE_SIZE. A session that runs keeps the size of its start. Superseded by 01M5SMW28KT5D5HPRH7KAZ8TVC.
The riff daemon
- 01M4ZDTVXY84ETYJWMWDMQ34CN
riff run ITEMdoes one work item in one rootless podman container, through the daemon. It asks the daemon for the image (the daemon pulls it when it must), then for the session. The host does each riff step with riff 1.x: it checks the facts, starts a worker session, claims the item, makes the clone, sends the verify request and releases the item. The daemon starts the container, writesevents.jsonl, and sends each event, with no secret, toriff run. No riff runs in the container. With no daemon,riff runstops before the claim, and says how to start the daemon. Ctrl-C asks the daemon to stop the container. Whenriff runends with no stop, the session goes on in the daemon. Superseded by 01M4ZKX0NXN7E33VC3PSFGSPDT. - 01M4ZHW2083VFTQJ772ZJ27XAC The daemon keeps its state in the
folder
statein its folder: the log and the checkpoints ofriff-state. At each start, it loads the newest checkpoint that it reads, and replays the log after it. At its first start, it makes the riff of its machine with a random ID. At a stop, it writes a checkpoint. Superseded by Wave B (01M519Z2JMAKN0T6JZFBPSTKC8). - 01M4ZHW22F8HE2E1WDSTNDJW4H The daemon runs one command at a time. The records of a command are in its log before the command gets its reply. When a write of the log fails for good, each next command fails until the daemon starts again. Superseded by the re-cut (01M53NGW3FR555TWP8Z8HHRVDJ).
- 01M4ZHW24JZZXM3JJXG3PRJS7N
riff daemon statusalso shows the ID of the riff of the daemon and the position of its log. Superseded by the re-cut (01M53NGW3FR555TWP8Z8HHRVDJ, 01M53NGW6F5HP1DZ9QRGP50MGT). - 01M4ZDTW25YV0GMMVGWN6JTJC5 Until
riff relay(#717), the input and the events of a session go through thepodman runof the daemon. At a stop of the daemon they end:claudeends after its turn, and its container goes.riff runthen says that the daemon stopped, finds the pull request, and releases the item. Superseded by 01M4ZKX0F25S284R0N7KZVTHNA. - 01M588HSXQ2KWX0V8RYM65CPFR The line of numbers of
riff topshows the 5-minute load against the limit of the monitor:load5 9.8 of 12. It has the warning style when the load is over the limit. This supersedes the load part of 01M421QPZ9E01PQ62PDBH378SJ. Superseded by riff 4.0 (01M599XR3NMJD9RAJ6ZAMW6KG5).
riff relay
- 01M4ZKX0H9N82ZBC9PKSPTQV4N Until #719, the daemon serves the riff
tools of a session on the riff-server of
riff run, as the worker session thatriff runstarted. That session holds the claim of the item. The daemon uses the sign-in of the person on its machine. A session gets only the tools that act on riff-server, and on no folder of the host and no claim:whoami,who,threads,read,post,tell,statusandblocked. The daemon refuses each other riff tool with a text that says why. Superseded by 01M4ZV3B6T2KB09WJ59F07HXET.
The lead, the view and the takeover
- 01M51KJ1V0STB4QCN4N5SBYGSP
holdhas the fieldinterrupt. With it, the relay writes the interrupt control request of stream-json on the stdin ofclaudebefore the close, as the Agent SDK does. The hello of the relay saysinterruptswhen the relay can do it. A relay of riff 1.5 reads aholdwithinterruptas its ownhold. The protocol stays 4. Superseded by 01M5RG6C25DB49ZKM03Y93QB1F.
The permission rules
- 01M5084K09Z2S7N57315CX2R0F
riff initchecks that the user settings of Claude Code ($CLAUDE_CONFIG_DIR/settings.json, else~/.claude/settings.json) have each rule of the user place. When rules are missing, it names each rule, and asks one yes or no question before it adds them.--yesanswers yes with no question. A no, or no terminal with no--yes, changes nothing, and the check fails.riff init --checknames each missing rule and asks nothing. riff adds only the missing rules, and keeps each other rule and key in its order. The rules of the user place name no repository and deny nothing. Superseded by 4.0 (01M59EWWDC7EBQEG02TVNN81HD). - 01M5084K2Q3HH4BK7DJMYQD7C1
riff initasks its question also when another check fails. After a yes, the check of the rules passes. When another check still fails, riff installs nothing and pulls nothing. Superseded by 4.0 (01M59EWWDC7EBQEG02TVNN81HD). - 01M51M3ZHHBE61CEG9T6ENFG18 With
update.auto = true, at each start the riff daemon adds each rule of the user place that the user settings of Claude Code lack, when its own binary is a release. It writes asriff initdoes, keeps each other rule and key in its order, and removes no rule. Withupdate.auto = false, it changes nothing.riff update --autois the only switch, and it asks no other question. Superseded by 4.0 (01M59EWWDC7EBQEG02TVNN81HD). - 01M51M3ZPAEMJ64266QSJBNQ46 The riff daemon logs each rule that
its sync adds, and one line of the result. It keeps that line in the
local files of riff.
riff initandriff init --checkshow whether the sync is on, and that line. Superseded by 4.0 (01M59EWWDC7EBQEG02TVNN81HD).
The steps of an update
- 01M5GT0QV72BZA4VVWDPR1WG17 riff keeps no command only so that the update of an older line can finish. When the update from the line before stops on the new riff, the book gives the step that a person runs by hand. Superseded by 01M5RG6C4H3YPACS41S6EPS87E.
- 01M5JFF7TAVA87TMJXK25HDJ19 riff init removes the plugin of riff 3 that is left on the machine: the folder claude-plugin in the data folder of riff, and the keys enabledPlugins.riff@riff, extraKnownMarketplaces.riff and a statusLine that runs riff statusline in the user, project and local settings of Claude Code. Each other key stays. riff init –check names them and removes nothing. The riff daemon of a release removes them at its start behind the gate of the update by itself, and riff update –check names them with the fix riff init. Superseded by 01M5RG6C4H3YPACS41S6EPS87E.
riff run
- 01M4Z81APKC14AVNJWCGRQKNVW
riff run ITEMdoes one work item in one rootless podman container, with no daemon. The host does each riff step with riff 1.x: it starts a worker session, claims the item, sends the verify request and releases the item. The container does the work. No riff runs in the container. riff shows each event ofclaudeon the terminal, with no secret (01M4Z60B8KDXD190RWC7D184Q3). Superseded by 01M4ZDTVXY84ETYJWMWDMQ34CN.
Data and peers
- 01M4Z60B8AFV68ZSAFA540G73C The state machine of riff-server becomes a library in the daemon. Superseded by the re-cut (01M53NGW3FR555TWP8Z8HHRVDJ).
- 01M4Z60B8BHAR9GHCC0M7V7CV5 The log has the shape of peer to peer: each author has an append-only signed log, and each type of record has one writer. A message or a status: its author. The plan and the waves: the lead of the repository. The members: the owner (01M4Z60B8C8JBV1T8T0W6ZKFE6). A claim: a compare-and-swap push to the forge (01M4Z60B8C3W5TMPVWNFXD1SYN). The keys are ed25519. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808).
- 01M4Z60B8C8JBV1T8T0W6ZKFE6 The owner signs the key of each member. riff 3.0 has no sign-in with an OpenID Connect provider, no OAuth client and no riff token. Superseded by the re-cut (01M53NGVB7SZJW7X8SYS3B0ZHG, 01M53NGW9FH02EM74GKY829808).
- 01M4Z60B8C3W5TMPVWNFXD1SYN A claim is the ref
refs/riff/claims/ITEMon the forge, for examplerefs/riff/claims/issue-12orrefs/riff/claims/verify-issue-12. It is not a branch. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808). - 01M4Z60B8DYAVEQF6F8N9QP7MS Each claim is a new commit: the empty
tree, no parent, no signature, and a message with the item, the user,
the session and the time. The daemon makes each claim and each
release, not the session. A claim is a create with
git push --force-with-lease=REF:: the empty value means that the ref does not exist yet. A release is a delete with the lease of the claim commit. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808). - 01M4Z60B8EB7A44A41CWWP7G8K A claim fails when its lease fails in
the client (
stale info) or on the server (cannot lock ref). riff reads both as: another session holds the item. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808). - 01M4Z60B8FQ3GMWQH12CXE0JS9 A claim ref has no time limit. It holds until its release, or until the lead deletes it (01M4Z60B8FFRMT3DVG8H1CCZ13). Superseded by the re-cut (01M53NGW9FH02EM74GKY829808).
- 01M4Z60B8FFRMT3DVG8H1CCZ13 The lead deletes the claim of a session that is gone, with the lease of its claim commit. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808).
- 01M4Z60B8G1VWGNKBH8ZJGJ7QJ In a public repository, each claim ref and each claim commit is public, also after the release. A claim commit holds no secret. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808).
- 01M4Z60B8HN1C1XAG1CA1YS36H Peer to peer comes right after local isolation works. The transport is iroh (#697). The first release with two peers uses the defaults of iroh: the DNS discovery and the public relays of n0. A small discovery service on Cloud Run comes when riff leaves those defaults. DNS discovery on comotechnologies.io is future work. Superseded by the re-cut (01M53NGW9FH02EM74GKY829808).
Wave B: the record
The requirements of Wave B (#803) say how the daemons of two or more people share one riff. A requirement of Wave B holds when the item of Wave B that builds it merges. A requirement that ends with “Superseded by Wave B (ID).” holds until then. The re-cut supersedes each requirement of Wave B (01M53NGW9FH02EM74GKY829808).
- 01M519Z1WKH6THH971AQWHH8CN A record is one change of the riff by one author. Its bytes are, in this order: the format version (1, one byte), the type (one byte), the riff ID (32 bytes), the key of the author (32 bytes), the sequence number (8 bytes), the hash of the record before it (32 bytes), the time of the author (8 bytes, in milliseconds since the Unix epoch), the length of the body (4 bytes), the body, and the signature (64 bytes). Each number is big-endian. The riff ID and the key of the author are ed25519 public keys. The body is the JSON of the type, in UTF-8.
- 01M519Z1Z1FVZQQ612DEMSQ7PG The signature of a record is an
ed25519 signature by the author over the tag
riff record 1and each byte of the record before the signature. A reader checks it withverify_strict. Each daemon keeps the bytes of a record as it got them, and never encodes a record again. The ID of a record is the BLAKE3 hash of its bytes. - 01M519Z21ECQY89RS60DZKZQVR The first record of an author has the sequence number 1 and a hash of 32 zero bytes. Each next record of that author has the next sequence number and the ID of the record before it. So the records of one author make one chain with no gap.
- 01M519Z23WX0A142N74BKAKKYY Each type of record has one writer and
a largest size of the whole record.
riff: the owner, when it makes the riff, 1 KiB.memberandremoved: the owner, 1 KiB.left: a member, for its own key, 1 KiB.session: the daemon of the session, for its start, its place, its lead mark, its claims, its status and its end, 4 KiB.message: the daemon of the session that posts, for a message, a note or a status request, 64 KiB.plan: a daemon of a lead, or the daemon of the owner, for a hold, a free, a pause and a resume, 4 KiB. - 01M51Q0JX87PPFGPABFPVWWW0E The byte of each type of record:
riff1,member2,removed3,left4,session5,message6 andplan7. The byte 0 is no type: a reader refuses it. Each other byte is a type that a reader does not know. - 01M51Q0JZR00GHDQSGGSAQR4HG A
sessionrecord holds the whole state of its session: its place, its lead mark, its claims, its status and its end. Its daemon writes a new record at each change. The last record of the session in the chain of its author counts. - 01M519Z26AP5NZSFHWRQ2QCV9D A reader skips a field of a body that it does not know. It keeps and passes on a record of a type that it does not know, after the checks of the bytes, the chain and the signature, with a largest size of 64 KiB. It does not apply that record.
- 01M519Z28S7FB4T1H52642JXZ1 The time of a record is the time of its author. riff shows it, and never orders records by it. Each daemon gives each message the next number of its own: the order in which it kept the messages. Another daemon can give the same message another number.
Wave B: the keys
- 01M519Z2BB0YJY5CRMB54JVYFA Each daemon has one ed25519 key. The daemon makes it at its first start, and keeps it in the OS keyring (R21). The private key never leaves the machine, and never goes into a log line, a message, a record or a file of a clone.
- 01M519Z2DSFGHNV5S88AYFK7PY The key of the daemon is also the key of its iroh endpoint. So one public key is the endpoint ID of the daemon, the author of each record that it writes, and the member key that the owner signs.
- 01M519Z2G77CT94Y6XPW9BCRK9 The key and the records of a daemon go together. When a daemon finds its key but not its own records, or its records but not its key, it makes a new key and a riff of its own. The owner then invites the new key. Recovery of a lost key is future work.
- 01M51BFYNMYHSQ62BBJES3CCWT A keyring that is locked or does not
answer is not a lost key. The daemon then waits for the keyring, as
riffdoes (01M4385CCATXC0B8HV1XD6EFWG): it looks again each 30 seconds, makes no new key and no riff of its own, binds no endpoint and writes no record. When the keyring answers, the daemon loads its key and goes on. Only a keyring that answers with no key of the daemon is a lost key (01M519Z2G77CT94Y6XPW9BCRK9). - 01M519Z2JMAKN0T6JZFBPSTKC8 The daemon keeps its state in the
folder
statein its folder: the records of each author, and the log and the checkpoints ofriff-state. At each start, it loads the newest checkpoint that it reads, and replays the log after it. At its first start, it makes its key and a riff of its own: the riff ID is its public key, and its first record isriffwith the user name of the person (USER). At a stop, it writes a checkpoint. - 01M519Z2N2PMZJ0CYAPV0A47BZ
riff daemon statusshows the public key of the daemon, the riff ID, and the user of the owner. - 01M51JW1XRZWC3Q7G32V0X0D89 The OS keyring keeps the key of a
daemon in the secret
daemon-key DIR, whereDIRis the folder of the daemon. So each folder of a daemon has its own key. - 01M51JW20CSAECFRZTJ95P6F3E Each error of the keyring is a wait of the daemon, also a keyring that riff cannot open. The daemon says the error in one line, and looks again each 30 seconds.
- 01M51JW233MXB72MHHRAX4E4NF The daemon serves at its start, and
reads its key in the background. While it waits for its key, it runs
its sessions, and
riff daemon statusshows the line of the wait. Until the records of Wave B, a session needs no key of the daemon. - 01M51WPSH4KBQMVT3XGDD45G63 The daemon says the line of its riff
(a first start, a new key, or the key that it read) only after it
has its riff and its records. So
riff daemon statusafter that line shows the key, the riff ID and the owner. - 01M51JW25SPDW564A3DG31D7XM When the daemon makes a new key
because its key and its state do not go together, it moves the old
folder
statetostate.lost-MSbeside it. riff never reads that folder.
Wave B: the sync
- 01M519Z2QE6QYPJX8HVY17GR6Z Each daemon binds one iroh endpoint at
its start, with the preset
N0: the DNS discovery and the public relays of n0 (01M4Z60B8HN1C1XAG1CA1YS36H). It keeps the endpoint bound while it runs. Its ALPN isriff/3, the major version of riff, so a daemon of another major version does not connect. riff takes iroh with no featuremetrics. - 01M519Z2SV9XSQA4N59N10JAQC A daemon keeps one connection to each other daemon of its riff that is online. Of two daemons, the one with the lower key connects. When a connection ends, it connects again: each try has a limit of 5 seconds, and the wait between two tries starts at 1 second and doubles up to 60 seconds.
- 01M519Z2WAGQEX1H4EGZXWBZT6 At each connect, each side checks that iroh gave the key of the other side, and that the key is the owner or a member of its riff that is not removed and did not leave. Else it closes the connection. The one other connect is a join (01M519Z3YA9WBH9TWQPAH6A64Z).
- 01M519Z2YSXADT45A1R0N70WRQ After the connect, each side sends its heads: for each author of the riff, the highest sequence number that it has. Then each side sends each record that the other side lacks, for each author in sequence order, the records of the owner first. After that, each side sends each new record at once: its own, and each record that it got from another daemon. So a record reaches each member through each daemon that has it.
- 01M519Z317AT7KT7Q643NVYZD1 A daemon checks each record before it
keeps it (01M4Z60B8K4C6DHFAKNCAWR91S), in this order: the size of its
type; the version and the type; the riff ID; the author is the owner,
or a member that the records of the owner admit and that is not
removed and did not leave; the sequence number is the next one of the
author; the hash of the record before it; the signature; the body is
the JSON of its type; the writer of the type. The user of a
sessionor amessagerecord is the user of the member key of its author. - 01M519Z33N2MW43XY62NYC6RRH A daemon does not keep a record that it cannot check yet: a sequence number after the next one, or an author that no record of the owner admits yet. The next heads give it again, in order.
- 01M51BFYQVCFEWCJZERBD4S5N5 A daemon drops a record that it has already: a record with the ID of a record that it keeps. It drops it before the checks of 01M519Z317AT7KT7Q643NVYZD1, with no fault: it logs nothing, and the connection stays. It does not apply the record again, and does not pass it on again. A daemon passes no record back to the daemon that sent it. So in a full mesh, a record that comes over two paths is kept one time, and each connection stays.
- 01M519Z363W5GV4MV5RDB6ECG0 A daemon refuses a record that fails a check. It closes the connection to the daemon that sent it, and logs the author, the sequence number and the check that failed, with no body. It connects again after the wait of 01M519Z2SV9XSQA4N59N10JAQC.
- 01M519Z38G3RAZPKGFA3R2CB3P A fork is two records of one author,
each with a good signature, with the same sequence number and other
bytes. The daemon keeps the first one that it kept, and keeps no next
record of that author. It keeps both records as the proof, shows the
author as forked in
riff members, and posts a note to the lead of its user. The owner removes the forked key, and the person joins again with a new key. - 01M519Z3AXFKBHJSAW46KWFTXS A daemon applies each record that it keeps to its state, in the order that it keeps them. The state of each type comes from one writer, so two daemons that keep the same records have the same state.
- 01M519Z3DB3MVP0FK3FBC9KSGD When a daemon keeps a message, also a
message of another daemon, it wakes each session of its own that the
toselectors of the message match.riff relay watchin a session waits for the next wake of the session on its daemon. - 01M519Z3FTAZ20DQKDTW09M6NS The daemon serves the riff tools of
each session that it owns on its own state, which it shares with its
peers. It does not use riff-server. A worker and a verifier get the
tools that talk:
whoami,who,threads,read,post,tell,statusandblocked. The lead also getslead,hold,free,pauseandresume. The daemon refuses each other riff tool with a text that says why. - 01M519Z3J7Z0M3PNDY249Q57FH A hold and a pause are
planrecords. A free names the ID of each hold record that it ends, and a resume names the ID of each pause record that it ends. An item is held while one hold record of it has no free that ends it. A repository, or the whole riff, is paused in the same way. A free or a resume ends a record only when its author is a daemon of the user of that record, or the daemon of the owner. Only the owner pauses and resumes the whole riff. So the holds and the pauses come from the set of records, not from their order: a free that a daemon keeps before its hold ends that hold when the hold comes. - 01M51QQGYDR9NZ702NFEQR512W The daemon applies each record to the
state of its riff of peers, and not to
riff-state. That state comes only from the set of records, with no clock and no presence of one machine.riff-statedecides a post by the live sessions and the clock of one server, so two daemons with the same records could get two states. - 01M51T470TNWVFF97E474FV7BY In the riff of peers, the user of a session of a daemon is the user of the key of that daemon, also when riff-server gave the session another user.
- 01M51T4734E3B3Z4BZZJQYYR32 A daemon that runs a session with a
lead mark may write a
planrecord, also when another lead mark names that mark. So the writer check of aplanrecord reads only the records of its author, and does not depend on the order of the records of two authors. - 01M51VZG27DXSRHBT8M14S7B9A The riff tools of a session of the
daemon with a riff-server act on that riff-server, as in riff 1.x:
each session of
riff run, of a wave and ofriff lead startthat has one.riff relay watchof that session waits for its wake on riff-server, and the riff of peers has no record of that session. The daemon serves the riff tools on its riff of peers (01M519Z3FTAZ20DQKDTW09M6NS) only to a session with no riff-server. So the work of one repository is on one riff only (01M519Z506XWTSV1CJ6CCVFXEF). Superseded by Wave C (01M51R8FXQFXWHRD374577HZ3H). - 01M51BFYT2ZGN4W5E5R87GPVCM A session takes the lead with a
sessionrecord that has the lead mark. The record names the ID of the record that gave the old lead of its user in the repository its lead mark, when its daemon knows one. The lead of a user in a repository is the session that runs with a lead mark that no other lead mark names. When two such sessions are left, the session of the daemon with the higher key counts. So no daemon orders the leads by a time (01M519Z28S7FB4T1H52642JXZ1). - 01M519Z3MMW5RKAR1R14PPQ4T9 A new member gets each record of the riff, from the first one. A prune of old records is future work.
- 01M51Z9ZT69AQ0V2PX8T439NA5 The heads of a daemon name, for each author, the last sequence number and the ID of that record. When the heads of the other side name a record that a daemon has with another ID, the daemon sends its own record with that number first, and no next record of that author. So the other side sees the fork (01M519Z38G3RAZPKGFA3R2CB3P).
- 01M51Z9ZX4P0HEH53YFJ6XAWST A daemon sends its heads on a link at the connect, at once when it drops a record that it cannot check yet (at most one time in each second), and again each 60 seconds. At the heads of the other side, it sends each record that the other side lacks.
- 01M51ZA001C8MZM4BTBYTXRJ8M A daemon sends the proof of each fork that it keeps first at each heads of the other side, and the proof of a new fork to each link at once. A daemon that knows the fork drops both records with no fault. So each daemon of the riff learns the fork.
- 01M51ZA02XQ31PWNBSD46MTKA6 The note of a fork goes to the lead of
the user of the key of the daemon, in each repository where that user
has a lead. It is a
messagerecord of the kind note, from the user of the key on the host of the daemon, with no session. With no lead, the log line of the daemon is the only report. - 01M51ZA05RTRE9TSMBP20AS20H A daemon takes a connection of each key that may link, also of a higher key, for example at a join. Its own connect to that key waits while that link runs. A new connection of a key takes the place of the old link of that key.
- 01M51ZA08MGF20MPHATHE81B44 A link that ends for a fault of a side does not start the wait of 01M519Z2SV9XSQA4N59N10JAQC again at 1 second: a key that is not a member, a refused record, a frame that the daemon cannot read, or a record that it cannot keep. Each other end does. A link that falls more than 4096 new records behind ends. Its next connect sends what the other side lacks.
- 01M51ZA0BDZNXBB7R5SB8CEGA9
riff daemon statusshows each peer link of the daemon: the start of the key, its user and its host, and its state. The state is up since a time, on a direct path or on a relay; connects again after a number of tries, and why; or waits for the other daemon to connect. A daemon that cannot bind its endpoint logs why, runs its sessions, and shows no peer links. - 01M51ZA0E79V2FPBMQ6F1MYZJ5 With
RIFF_PEER_NET=local, the daemon binds its iroh endpoint on 127.0.0.1, with no relay and no DNS. It finds a daemon only by an address that it gets. Each test of riff sets it, so no test reaches n0.
Wave B: the members
- 01M519Z3Q27V93QTFKXPQX42J1 A riff of Wave B has an owner and members. The owner is the key that made the riff. The owner role does not pass to another key, and the riff has no admin: both are future work. A member is one key, with one user and one host. A user can have many keys: one for each machine.
- 01M519Z3SGEHVMS85HZD169CAP
riff invite USERmakes an invite for one new key of the user USER. Only the owner runs it, at the CLI, with a terminal (01M4Z60B8JEX15M1Q2435FZSDM). It prints one invite string:riff1-and the base32 of the riff ID, the user, a random secret of 32 bytes and the end time. An invite works one time, for 24 hours. The daemon of the owner keeps the hash of the secret, the user and the end time in its folder. They go into no record. - 01M519Z3VXY4R3WDSSBQCVYTS4 An invite string is a secret until it
is used or ends: a person who has it can join one time as USER. The
owner sends it to the person by hand.
riff invitesays so. - 01M52JPVT5DY1VJQMQCNY9Z0PC The user of the owner stays
USERof its machine (01M519Z2JMAKN0T6JZFBPSTKC8), also when the riff user of the person has another name. A new key gets the user of its invite. So the book says to add a second machine of a person withriff inviteof the user thatriff membersshows for that person (#846). - 01M519Z3YA9WBH9TWQPAH6A64Z
riff join STRINGon the machine of a person: the daemon connects to the daemon of the owner by the riff ID, and sends the secret and its host name. The daemon of the owner checks that the invite is known, not used and not ended. Then it writes amemberrecord with the key of the new daemon, the user of the invite and the host. The new daemon gets the records of the riff and syncs with each member. The daemon of the owner must be online. A failed join changes nothing, and says why. - 01M519Z40SHPJ1BB2SZN9AW43F A daemon is in one riff at a time.
riff joinrefuses while the riff of the daemon has another member. The records of the old riff stay in the folder of the daemon. - 01M519Z435DTR693NAK5GSB6XA
riff leaveon a member: the daemon writes aleftrecord, sends it to its peers, ends its peer links, and makes a riff of its own with the same key. The owner cannot leave. Each session of the daemon leaves the riff with it. - 01M519Z45J9RC734KBY1G8D49V
riff remove USERwrites aremovedrecord for each key of USER, or for one key with--host HOST. Only the owner runs it, at the CLI, with a terminal. The record names the highest sequence number of that key that the daemon of the owner has. Each daemon then refuses each connection of that key, and each record of it after that number. The owner cannot remove itself. - 01M51RGNHBCY7GDSCKPE8REY6A When a daemon keeps a
removedrecord, it drops each record of that key after its last number that it kept before. Its book holds them no more, and its state comes again from its other records. So the order of theremovedrecord and those records does not change the state. - 01M519Z4823WC7D3VVTFSDYBS4
riff membersshows the owner and each member key: the user, the host, the start of the key, online or the time when it was last seen, and forked, removed or left. - 01M519Z4AEMDFGBF22RZ3HQR1R Until Wave C, the member commands of
riff-server 1.x stay with the flag
--email:riff invite --email EMAIL,riff remove --email EMAILandriff members --email. Superseded by Wave C (01M51R8G06XMJF3GAP7MFW06R0), and by 01M588J2QAF288ZB3M9PBN71ZS. - 01M588J2QAF288ZB3M9PBN71ZS The member commands of riff-server take
the email as an argument, with no flag:
riff invite EMAIL,riff remove EMAILandriff members. They have no flag--email. - 01M521J6CEJB24QFN17077KG9S An invite string is
riff1-and the base32 (RFC 4648, lower case, no padding) of these bytes: the riff ID (32 bytes), the secret (32 bytes), the end time in milliseconds since the Unix epoch (8 bytes, big-endian), and the user in UTF-8. - 01M521J6F5QH9MW4GVY0H2JD8N The daemon of the owner keeps each
invite in the file
invites.jsonof its folder, with the mode 0600: the BLAKE3 hash of the secret, the user, the end time, and whether a join used it. A join marks the invite used before the daemon writes thememberrecord. When that write fails, the daemon gives the invite back. - 01M521J6HX5BN0FNDBSKZDVQE7 A join is a connection to the daemon of
the owner with the ALPN
riff/3/join. The new daemon sends the secret and its host on one stream. The daemon of the owner checks, in this order, that it is the owner, the key, the host and the invite, and answers with the join or why not. After a join, the connection goes on as the peer link of the two daemons. - 01M521J6MKSBAA2QP6ZFHYT9ZQ A key has one
memberrecord. The daemon of the owner refuses a join of a key that its riff knows: the owner, a member, a removed key, or a key that left. The writer check of amemberrecord refuses the key of the owner and a key that the riff knows. So the user of a key does not depend on the order of the records. Superseded in part by 01M5328FPJT7Q5Q1509BC5F0J7 and 01M5328FVZJGCY9VPQFERM7P81: a key that left joins again. - 01M5328FPJT7Q5Q1509BC5F0J7
riff leaveonly leaves. A key that left joins again with a new invite string of the owner. The daemon of the owner then writes a newmemberrecord of the key, with the user and the host of the new invite, and withafter: the sequence number of theleftrecord of the key. It writes it only when it kept thatleftrecord. The records of the key after that number count with the new user. The first of them names the newmemberrecord as the record before it. So a record that the key signed after itsleftrecord and before the newmemberrecord stays refused. - 01M5328FS8XGVW3X6N94F65FA8 The daemon of the owner refuses a join
of the key of the owner, of a member key, and of a removed key. The
refusal names which of the three. A key that
riff removeremoved stays refused, also with a new invite: a remove is the choice of the owner. - 01M5328FVZJGCY9VPQFERM7P81 Each
memberrecord of a key starts a join of the key. The user of a record of the key is the user of its join: the lastmemberrecord whoseafteris lower than the number of the record. The writer check of amemberrecord reads only the records of the owner: a firstmemberrecord needs a key that the riff does not know, and amemberrecord withafterneeds a key with amemberrecord, not removed, and anafterhigher than theafterof its lastmemberrecord. So two daemons that get themember,leftand newmemberrecords of a key in different orders agree on the user and the state of the key. - 01M532QC66ER7BZPYWR7QDXAP1 A daemon that joined again writes no
record until it has its own records up to its
leftrecord: the record would fork its chain. Atriff leave, a removed daemon writes noleftrecord, because each daemon refuses it, and it still gets a riff of its own. - 01M521J6QBQFRDWRPR0NGHE7TM
riff joinandriff leaveneed a terminal too, asriff inviteandriff removedo. An agent session has no terminal, so it cannot move the daemon of the machine to another riff. - 01M521J6T2PWA9JREDXJK6DZ0V The
riffrecord also holds the host of the daemon of the owner. Ariffrecord with no host shows no host. - 01M521J6WZT2KR5ZQE9DZ9A5AC The file
state/joinedof a daemon holds the riff ID of the riff that it joined. With no such file, the daemon is in its riff of its own. The records of its riff of its own are instate/records, and the records of a joined riff are instate/riffs/RIFF/records. Each start of the daemon opens the riff of that file.riff daemon statusshows the riff that the daemon is in, and the user of its owner. - 01M521J6ZRNWBSPK7WB23TVY59 At a join and at a leave, each session
of the daemon on the riff of peers writes its end record in the old
riff, and its start record in the new riff. At a leave, those end
records come before the
leftrecord. Each peer link sends theleftrecord, ends its stream, and waits up to 5 seconds for the other daemon to close the connection. Then the daemon is in its riff of its own again. - 01M52JF709YHYE7SG6V3SDHR74 At
riff leave, the daemon goes to its riff of its own only after a peer link sent theleftrecord and no link runs, or after 5 seconds. A link sent the record only when the other daemon then closed it with the code of a key that left: it does so when it keeps the record, and at the end of the stream of a key that left. A link that a new connection replaced does not count, and a moment with no link does not end the wait. A link that starts during the leave sends theleftrecord with the records that the other daemon lacks, and then ends its stream. When no link sent the record in 5 seconds,riff leavesays so, and names theriff removecommand that the owner runs. - 01M521J72EC8Z5HAKA1Q9P5Z24
riff membersshows a key that has no link as last seen at the later of two times: the end of its last link since the start of the daemon, and the time of its last record.
Wave B: the claims
- 01M519Z4CT0PRXPRM22037Z5RY The daemon makes each claim and each
release (01M4Z60B8DYAVEQF6F8N9QP7MS) in the main clone of the
repository, with the git sign-in of the person. The message of a
claim commit is
riff claim ITEM, then the trailersRiff-Item,Riff-User,Riff-Session,Riff-Key(the public key of the daemon) andRiff-Time(UTC, RFC 3339). - 01M519Z4F7Z87BXQYT7F8EABCN The
sessionrecord of a session names its claims. So each peer shows the claims inwho,topandviewwith no call to the forge. The forge decides: before a wave picks an item, it readsgit ls-remote origin 'refs/riff/claims/*'. - 01M519Z4HK333JWVXAWQHCGK6M The daemon releases the claim of a session at the end of the session. At its start, a daemon deletes each claim whose commit names its key and whose session does not run.
- 01M519Z4M0F2RY0VDQNB2QFAPJ The daemon of the lead of a user deletes a claim of another session of that user, with the lease of its claim commit (01M4Z60B8FFRMT3DVG8H1CCZ13), when the daemon of the session wrote the end of the session, or when it had no connection to that daemon for 15 minutes. A claim of another user whose daemon is offline holds.
- 01M519Z4PESWFV8Y03SKTSGV4Q
riff release ITEM --forgeat the CLI deletes a claim with the lease of its claim commit. A person deletes a claim of their own user. The owner deletes each claim. It refuses a claim of another user for a member. Superseded in part by Wave C (01M51R8G59KA4WFCD7CBAYP5DN). - 01M51BFYW85YBEWAB20YTCJQFK Until Wave C,
riff claim ITEMandriff release ITEMact on riff-server 1.x, asriff --helpsays today. Onlyriff release ITEM --forgeacts on a claim of riff 3.0 (01M519Z4PESWFV8Y03SKTSGV4Q). A person makes no claim of riff 3.0 at the CLI: the daemon makes each one. Superseded by Wave C (01M51R8G06XMJF3GAP7MFW06R0). - 01M519Z4RWHWP3TKNP4T5GKKE9
riff runand the waves of the daemon do each riff step with the daemon, and with no riff-server: the daemon claims the item on the forge, the session talks through the state of the daemon, and at the end the daemon sends the verify request to the lead as amessagerecord and releases the item. This replaces the steps with riff 1.x of 01M4ZKX0NXN7E33VC3PSFGSPDT and 01M4ZV3B6KP33YV750DJ89SMFF. - 01M5232XDH1B3BGX7FXT0A2BRE The daemon keeps the main clone of each
repository where it made a claim in the file
forges.jsonof its folder, by repository. It writes the main clone there before the push of the claim. The release at its start (01M519Z4HK333JWVXAWQHCGK6M) and the step of the lead (01M519Z4M0F2RY0VDQNB2QFAPJ) read the claims on the forge of each main clone of that file and of each wave of the daemon. The release at its start deletes only a claim with aRiff-Timebefore the second of its start: a claim of this run of the daemon stays, for example the claim of a job of a wave before its session runs. - 01M5232XGM1GYENFCWBQ5TFYBN The daemon of a lead looks at the
claims of its user each minute. A claim of a session with no
sessionrecord counts as a claim of a session that runs. When the daemon of the lead had no link to the daemon of the claim since its own start, the 15 minutes count from that start. - 01M5232XKPG96CTQ7HSAM4726S
riff release ITEM --forgeasks the daemon of the machine to delete the claim on the forge of the main clone of the working folder. The daemon decides with its riff of peers (01M519Z4PESWFV8Y03SKTSGV4Q). A ref whose commit is not a claim commit of riff names no user: only the owner deletes it. Superseded in part by Wave C (01M51R8G59KA4WFCD7CBAYP5DN). - 01M526Z1Q3QM3X0C7N6MCX8Z6T
riff run ITEM --peersandriff wave run --peersdo the steps of 01M519Z4RWHWP3TKNP4T5GKKE9. With no--peers,riff runandriff wave runuse riff-server 1.x until Wave C (01M519Z506XWTSV1CJ6CCVFXEF). A wave with no riff-server keeps an empty riff-server, and the daemon picks the world of each wave and of each session by it. Superseded in part by Wave C (01M51R8G59KA4WFCD7CBAYP5DN). - 01M526Z1SVG3ZK38GH51AHCRHP
riff run ITEM --peersdoes the checks before the claim, then asks the daemon to do the build of ITEM as a job of a wave, in the room of the host. It shows each line, each event and the end of the session, then a line for each step after the end. A SIGINT or a SIGTERM asks the daemon to stop the container. The daemon does the steps after the end in each case, soriff runhas no end step of its own. - 01M526Z1WH3865PKGFPYPFWQT7 A job with no riff-server does its
steps after the end in this order: the verify request, the note or
the result of a verify, as a
messagerecord to the lead; then the release of its claim on the forge. The end of the session writes the end record of the session, and releases no claim of a job. - 01M526Z1Z80JFAS0GS04PVF1KG The steps after the end of a job with no riff-server wait for the riff of the daemon: for example a job whose session ended while no daemon ran, after a new start of the daemon that waits for its keyring.
- 01M5274Z2NQEKN6B4H7V95KKW0 On the riff of peers, a step of a lead
(
hold,free,pause,resume) needs the lead, or a session of the daemon of the owner that is not a worker. A worker of the daemon of the owner is refused, as each worker (01M519Z3FTAZ20DQKDTW09M6NS). - 01M527NXZ6KRFAA641ZKPVGC4V Each git call of a claim, a release
and a read of the claims runs with
GIT_TERMINAL_PROMPT=0: git asks for no sign-in, and a push or a fetch with no sign-in fails at once. - 01M526Z224A1CVPJQE8W7ERXV6 A session with no riff-server has no riff-server in the environment of its auto mode (01M516HETNZCBFBJNHT8BKFB1Y).
- 01M5232XPKES7R8P3F3H8X6V4B A claim reads as held only when the
forge refuses its ref for a failed lease:
stale info,cannot lock ref, a ref that is there already, orfailed to update ref. Each other refusal and each fault of git is an error, and riff claims nothing. A push of a claim or of a release runs no pre-push hook of the clone (git push --no-verify).
Wave B: the scope and the way over
- 01M519Z4V991WXHQ9WRANDF63N One riff for each owner. It holds each
repository that a session of a member works in: one thread for each
repository, and the thread
chat. Each member reads each thread. - 01M519Z4XPS0AE026ZC3CPATP5 riff-server 1.x runs on until Wave C. A daemon of 3.0 does not talk to it, and riff-server does not see the riff of 3.0. A session of 1.x and a session of 3.0 do not see the messages or the claims of each other. Superseded by Wave C (01M51R8H8W8ZBEBXFKWTK7ZB14).
- 01M519Z506XWTSV1CJ6CCVFXEF The work of one repository runs on the riff of 1.x or on the riff of 3.0, not on both at one time. riff does not check it. Mike decides when a repository moves. Superseded by Wave C (01M51R8GQ8AKGGEEBYPS1ED8RQ).
- 01M519Z52NHWATA0D61N8A2C3V iroh encrypts each connection between two daemons. The relays of n0 see the endpoint IDs, and no record. Each member reads each record of the riff, so a record holds no secret (01M4Z60B8KDXD190RWC7D184Q3).
- 01M519Z554SC3EAT69Q3M6JDAP
riff viewandriff topshow each session of each daemon of the riff: the user, the host, the item, the state and the status. The sessions of a daemon that is offline show as offline, with the time when the daemon was last seen. A takeover works only for a session of this machine. - 01M522MRYR89SY6WRV720806RA A daemon of the riff is online while
this daemon has a peer link to it. Else it was last seen at the later
of two times: the end of its last link since the start of this
daemon, and the time of the author of its last record.
riff viewandriff toptake the sessions of the riff of peers from the status of the daemon of this machine.riff topadds them to the sessions of riff-server. With no daemon, it shows the sessions of riff-server only. A takeover of a session of another daemon, inriff viewor withriff takeoverand its session ID, refuses with a text that names the host of that session. Superseded in part by Wave C (01M51R8G7R46NC07YVWYJ211NM). - 01M519Z57K3SPRN8Z517VZ62BB The book page “riff 3.0: peers” (#803) has the picture of a riff of peers, the steps to share a riff, and a mermaid diagram of the sync, of a join and of a claim. Each item of Wave B adds its how-to to it. Superseded in part by the re-cut (01M53NGWJ48MMYPQNJQ0EY9T47).
Wave C: what goes and what stays
The requirements of Wave C (#879) say how riff 3.0 ends riff-server and the cloud parts of riff. The book has no page for this plan: its items wait in the Backlog (#881 to #889). A requirement of Wave C holds when the item of Wave C that builds it merges, or when its step is done. A requirement that ends with “Superseded by Wave C (ID).” holds until then. The re-cut puts the items of Wave C that end riff-server in the Backlog (01M53NGWN190T2MYB1EZAD36VB).
- 01M51R8FXQFXWHRD374577HZ3H Wave C removes riff-server and each
part that exists only for it: the crate and the binary
riff-server;riff cloud; the jobsDeployandStageof CI; the filesdeploy/alert.jsonanddeploy/lifecycle.json, theDockerfileof riff-server, and at the end the settings filesdeploy/cloud(01M51TD2ZCA717T5BWB72QTBTK); the sign-in of a person with OpenID Connect and its OAuth client; the design pages of riff-server (design-storage.md,design-engine.md,design-link.mdanddesign-plan.md); and, in the Google Cloud projectcomo-riff, the Cloud Run services, the buckets, the service accounts, the Secret Manager entries, the image repository, the sign-in of GitHub Actions and the alert of each instance. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8G06XMJF3GAP7MFW06R0 Wave C removes each part of
riffthat acts only on riff-server, with its tests and its book text:riff login,riff logout,riff server, the flag--serverandRIFF_SERVER, the device key,riff claim, the riff-server form ofriff release, the member commands with--email,riff admin,riff owner,riff step,riff audit,riff moveof the bridge release, and the shell forms of the session tools:riff who,riff whoami,riff read,riff post,riff tell,riff status,riff blocked, andriff leadwith no subcommand. The pagesstart-a-team-riff.mdandjoin-a-riff.mdgo. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8G2RZAVZN1432EGZSW0X Wave C removes the parts of riff 1.x
for a session on the host, with their tests and their book text: the
plugin of Claude Code with its skill and its hooks,
riff connect,riff enable,riff disable,riff mcp,riff watch, the status line, the workers in tmux (riff workers, the rollout, the clear, the reap, the pool of build jobs and the monitor),riff worktrees,riff lead compactandriff lead blocked. Each session of riff 3.0 runs in a container of the daemon (01M4Z60B7NW4GHKGCYZZDG26R6). The user place of the permission rules goes. The project place (riff setup) and the session place stay. Superseded by the re-cut (01M53NGVMD2PGEC0P9S3X4CTHT, 01M53NGVQDKCTNMG54YDFE22T2, 01M53NGWN190T2MYB1EZAD36VB). - 01M51R8G59KA4WFCD7CBAYP5DN After Wave C, riff has these commands:
riff init,riff daemon,riff run,riff wave,riff lead start,riff view,riff takeover,riff image,riff update,riff setup,riff pr,riff verify,riff usage, the hiddenriff relay, the peer commands of Wave B (riff invite,riff join,riff leave,riff remove,riff membersandriff release), and the commands of a person of 01M51R8G7R46NC07YVWYJ211NM.riff release ITEMacts on the claim on the forge (01M519Z4PESWFV8Y03SKTSGV4Q), with no flag--forge.riff runandriff wave rundo the steps of 01M519Z4RWHWP3TKNP4T5GKKE9, with no flag--peers. The cratesriff,riff-core,riff-state,hygiene,reqs,real-sessionandisolatedstay. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8G7R46NC07YVWYJ211NM After Wave C, a person at the CLI has
riff top,riff view,riff chat,riff tail,riff pause,riff resume,riff plan holdandriff plan free. Each one acts on the daemon of this machine and its peers, with no riff-server. Only the owner pauses and resumes the whole riff (01M519Z3J7Z0M3PNDY249Q57FH). Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8GAE5E6ZKD4PSNJYGGMW After Wave C,
riff-stateserves only the daemon. The parts ofriff-corethat only the HTTP API of riff-server uses go: its wire types, DPoP and the signed messages of the device key. The format fixtures of riff-server go with it. The test environmentisolatedstarts no riff-server. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8GD2EY0A0GF2X82PMA5B Each item of Wave C that removes a part marks each requirement of that part that has no mark yet with “Superseded by Wave C (ID).”, where ID is the requirement of Wave C that removes it. No requirement is deleted or renumbered.
- 01M51R8GFNC8Y3PY290CPZ2SY0 A release of riff 3 has the files
riff-x86_64-unknown-linux-muslandSHA256SUMS, and no riff-server. The job “Static riff” builds and checks onlyriff.riff updateinstalls onlyrifffrom such a release. Superseded by the re-cut (01M53NGVB7SZJW7X8SYS3B0ZHG). A release of riff 3 has riff-server.
Wave C: the order
- 01M51R8GJ7XTR40J95F99DSQVN Wave C has five steps, in this order:
the bridge release (01M51R8GMRND1SFCYZSM53KS5M), the move of each
repository (01M51R8GQ8AKGGEEBYPS1ED8RQ), the stop of riff-server
(01M51R8H8W8ZBEBXFKWTK7ZB14), the go-back window
(01M51R8HBB98JQ6QG6RFZWBY74), and the end
(01M51R8HK0VPBD4W6CKACWC1WW). Each step starts only when the step
before it is done. The items of Wave C that remove code merge on
mainafter the stop. No release of riff 3 comes before the end. Superseded by the re-cut (01M53NGS8FGRNY12DDJA3P0GH2, 01M53NGVTE186GE5E48E68N0BY). - 01M51R8GMRND1SFCYZSM53KS5M The bridge release is the last minor
release of riff 1.x. It has each item of Wave B, and the items of Wave
C that the move needs: the update with no riff-server
(01M51R8HTE7X1Q5NKZAS16E9PD, 01M51R8HZF5KQ28PBJA2DZZYKJ),
riff moveandriff cloud delete --all. riff-server runs it. Before the first item of Wave C that removes code merges, an admin points the branchrelease/1.xat the tag of the bridge release. A patch of riff 1.x then goes onrelease/1.x(01M4YJ6KGBPTCSVMFQCDKACZPV). Superseded by the re-cut (01M53NGVTE186GE5E48E68N0BY). - 01M51R8GQ8AKGGEEBYPS1ED8RQ riff 1.x and riff 3.0 run side by side
from the bridge release to the stop of riff-server. The work of one
repository runs on one of them at a time, and riff does not check it.
A repository moves only after the live check of Wave B (#846) passed.
The repositories move one at a time, in this order:
como-technologies/riff, thenn8behavior/dotfiles, thencomo-technologies/strata. Mike decides when each of his repositories moves. Mike and Brett decide together whencomo-technologies/stratamoves. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB).
Wave C: the move of a repository
- 01M51R8GSP3ZAJKGCY0KYGQYXT
riff movein a clone moves the work of its repository from riff-server to riff 3.0. It is a command of a person at the CLI, not themovetool of a session. First it checks, and a failed check changes nothing: the riff daemon of the machine answers;riff.tomlof the repository pins a session image (01M4Z7SP3KSKJYKNRP1MQ70C4Q); riff-server answers and the person is signed in; no session holds a claim in the repository on riff-server; and no worker of riff 1.x runs for the repository. Each failed check names its fix. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8GW67GFMFZ6S2MAAV6AX After its checks,
riff movepauses the repository on riff-server. It copies each hold of the repository on riff-server into a hold of the daemon, with the same item and reason (01M519Z3J7Z0M3PNDY249Q57FH). It turns riff 1.x off in the repository, asriff disabledoes. It posts a note to the thread of the repository on riff-server and on the daemon: who moved it, when, and thatriff move --backundoes it. Then it names the next step:riff lead start. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8GYRF329RKH4F7HP1TAA
riff movecopies the memory of the lead of riff 1.x into the Claude folder of the lead of riff 3.0, when that folder has no memory yet. The memory of the lead of riff 1.x is the foldermemoryof the project of the main clone, in the Claude folder of the person. riff reads the Claude folder of the person, and writes nothing there (01M4Z60B85X62PNYSVJWKXEWH3). Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8H1BXVQ5D8SRTFFQBWDQ
riff move --backundoes a move while riff-server runs. It stops the wave of the daemon and the lead of riff 3.0 of the repository. It resumes the repository on riff-server, removes the entry thatriff movewrote to turn riff 1.x off, and posts the note on both threads. The holds of the daemon stay. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8H6CD0C49GQW46QVK2VH The state of riff-server does not move
to riff 3.0, except the holds that
riff movecopies. riff 3.0 starts with no message, no thread and no claim of riff-server. A repository moves with no open claim on riff-server. The sign-ins, the tokens and the device keys of riff-server go with it. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB).
Wave C: the end of riff-server
- 01M51TD2ZCA717T5BWB72QTBTK The owner runs each
riff cloudstep of Wave C with the riff of the bridge release, in a clone ofcomo-technologies/riff: the stop, the go-back and the delete of the cloud parts.riff cloudreads the settings file of each instance fromdeploy/cloudof that clone (R144). Sodeploy/cloudstays onmainuntil the end: the item that removes riff-server keeps it. Afterriff cloud delete NAME --allof each instance, the live check of the end removesdeploy/cloudfrommain, before the release of riff 3.0.0. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8H8W8ZBEBXFKWTK7ZB14 The owner stops riff-server with
riff cloud delete shared: the service goes, and the bucket and each other part stay. The owner stops it only when each repository of riff-server with work moved, and no session of riff 1.x is live. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HBB98JQ6QG6RFZWBY74 For 7 days after the stop, each cloud
part stays: the go-back window. In that time, the owner brings
riff-server back with its state with
riff cloud deploy shared TAG, where TAG is the tag of the bridge release, and the email of the owner is inRIFF_OWNER(01M3JN3ASSV9SA0QZKXXJ0RTEV). Thenriff move --backin a clone moves a repository back. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HDY6BVG735NDKDM39Y5
riff cloud delete NAME --alldeletes each part of the instance NAME: the service and its domain mapping, the alert and its channel, the service accounts, the secret, the bucket, the GitHub environment of the deploy, and, for the instanceshared, the repository variablesCLOUD_DEPLOYandRIFF_OWNER. The image repository and the sign-in of GitHub Actions go when no other instance of the settings folder uses them. It asks for the name first, asriff cloud deletedoes. It says what it deleted and what was gone already, so a second run deletes nothing. It keeps the project and the settings file. With--project, it then deletes the Google Cloud project, after the person types its ID. Google keeps a deleted project for 30 days. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HGD8TV6BMHNZ3QCXFWZ Before
riff cloud delete --alldeletes a bucket, it writes each record of the log of the instance as text, asriff-server logprints them, into the filearchive/NAME-log-DATE.txtin the data folder of riff, with mode 0600. It writes no token and no secret. riff never reads the file: it is for people. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HK0VPBD4W6CKACWC1WW The end comes after the go-back window.
The owner runs
riff cloud delete NAME --allfor each instance thatriff cloud listshows, with the riff of the bridge release. Then riff 3.0.0 releases (01M51R8J200TMFS4ANDN9ZE6TB), and the line 1.x retires (01M51R8J4G95J1HPG16SRG25JY). Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HNF5GDNAHMEG7W2Q273 The GitHub App
riff-como-technologiesof riff 2.0 goes at the start of Wave C. No part of riff uses it since riff 2.x retired (01M4Z60B8N9W9TBE7KKDJQAN2C). The owner of the GitHub organization deletes it in the settings of the organization: only the App itself can delete it with the API of GitHub. Superseded in part by the re-cut (01M53NGWN190T2MYB1EZAD36VB): Wave C waits in the Backlog, so the owner of the organization deletes the App at any time. - 01M51R8HQZJV6PFTAMACVDWDBR Each step of Wave C has a way back
until the end. A release goes back with
riff update --tagof the release before it. A move goes back withriff move --back. The stop goes back in the go-back window (01M51R8HBB98JQ6QG6RFZWBY74). After the end, riff 1.x does not come back: a fault of riff 3.0 gets a fix in a release of riff 3. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB).
Wave C: the update and the line 1.x
- 01M51R8HTE7X1Q5NKZAS16E9PD With
update.autoon, the daemon looks at the release that GitHub marks Latest at its start and each hour. When it is a release of riff 3 or later, and newer than the riff of the daemon, the daemon installs it asriff updatedoes, and starts again after its last session (01M517BTP6BAGTRBYWPX9GPVE0). A release of riff 1.x still comes only from riff-server (01M510NWBNC20GXFRMHPW12TW9). Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HWXNCSXEWR95T0HHJ5B After Wave C,
riff updatewith no--taginstalls the release that GitHub marks Latest. riff never installs an older release by itself. Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8HZF5KQ28PBJA2DZZYKJ When
riff update, by a person or by itself, installs a release of riff 3 or later over a riff of 1.x, it first removes the parts of riff 1.x thatriff connect,riff enableandriff initwrote in the Claude config of the person: the plugin of riff and its marketplace, eachenabledPluginsentry of riff, the status line of riff, and each allow rule of the user place of riff 1.x. It keeps each other entry and key in its order, and says what it removed. The settingupdate.autois the consent of the person for this step (#855). So no hook of riff 1.x runs after the update. The riff 1.x that runs the update does this step. riff 3 writes nothing in the Claude config of the person (01M4Z60B85X62PNYSVJWKXEWH3). Superseded by the re-cut (01M53NGWN190T2MYB1EZAD36VB). - 01M51R8J200TMFS4ANDN9ZE6TB riff 3.0.0 releases at the end of Wave
C, as the release that GitHub marks Latest. Each machine with
update.autoon takes it by itself (01M51R8HTE7X1Q5NKZAS16E9PD). On each other machine, the person runsriff updateone time. Superseded by the re-cut (01M53NGVTE186GE5E48E68N0BY). - 01M51R8J4G95J1HPG16SRG25JY At the end of Wave C, the line 1.x
retires: each GitHub release of riff 1.x becomes a prerelease with a
first line that says that the line is retired (#795). No patch of 1.x
comes after that. The branch
release/1.xstays as history. Superseded by the re-cut (01M53NGVTE186GE5E48E68N0BY). - 01M51R8J6ZRE60NYEJX9KS2XWD The book page “riff 3.0: no cloud” (#879) says what goes and what stays, the order of the steps with a mermaid diagram, how a person moves a repository, the end of riff-server, and how to go back. Each item of Wave C adds its how-to to it. Superseded by the re-cut (01M53NGWJ48MMYPQNJQ0EY9T47).
The security of riff’s own code
- 01M4Z60B8K4C6DHFAKNCAWR91S The daemon checks each peer and each record: iroh checks the node key, each record carries the signature of its author, and the daemon checks the size and the type of each message before it applies it. Superseded by the re-cut (01M53NGW3FR555TWP8Z8HHRVDJ).
Process
- 01M4Z60B8MRBB8WTCVNE0G22JG riff 1.3 runs the riff while we build
3.0. It stays up, and nothing forces it off. Its patches go on
release/1.x(01M4YJ6KGBPTCSVMFQCDKACZPV). If 1.3 does not hold, we build 3.0 with no riff until 3.0 runs its own work. Superseded by Wave C (01M51R8GJ7XTR40J95F99DSQVN). - 01M4Z60B8N9W9TBE7KKDJQAN2C riff 2.x is retired.
mainwent back to the line 1.x (#705), and 3.0 builds on 1.3.1. 2.x stays only in the git history and in the tagsv2.0.0andv2.0.1. A part of 2.0 that 3.0 wants comes back by a cherry-pick. - 01M4Z60B8R57HG5Z0BA8XM3EM8 Each requirement of the server, the
cloud, the sign-in, tmux, the workers and their clear, the tidy of
worktrees and the pool of build jobs ends with “Superseded by 3.0
(ID).” It holds for riff 1.x until riff 3.0 replaces it. No
requirement is deleted or renumbered. The requirements of the GitHub
App and of the 2.0 sandbox left
mainwith 2.x (01M4Z60B8N9W9TBE7KKDJQAN2C). Superseded in part by the re-cut (01M53NGVB7SZJW7X8SYS3B0ZHG). - 01M4Z60B8RES9JSQDHNBFVR8AH After 3.0 replaces 1.3, the Cloud Run service, the GCS bucket, the Secret Manager entries and the GitHub App of riff go. Mike decides when. Superseded by Wave C (01M51R8HK0VPBD4W6CKACWC1WW).
The release cycle
- 01M58KMMHH70JEMBX3EYT9H3WM The watch of the lead looks at the
release cycle each 5 minutes, in a clone with
deploy/release-check.sh. Each new state of the current release wakes the lead with the state and the next step. The first look after a start only learns the state. Superseded by 01M5RGCW5NCKQYA61RM63914TX and 01M5RGD0RTXYA7FF5K1TH72MGX.
The UIs
- 01M54217ET3G7R4ZJ33CQRE5P9 riff 3.0 (#953) comes first, with the patch releases that make it stable. riff 3.1 comes after a stable 3.0. It moves the core library out of the CLI in small steps. Each step keeps each test and changes no command. The UIs come after riff 3.1.