Every change runs on a set route.
RiskRails lets an AI agent work on legacy production code. Each change is weighed on six axes before the agent edits a file, then runs light, guarded or full. A hook refuses an edit outside the task, a read of a production secret, a force push or a deploy.
npx riskrails@3.1.0
Watch two tasks
request add a 3% surcharge to card paymentsroute full
One line from setup to your commit.
The same commands in Claude Code and Codex. Most days you type only /rr-build. It plans first when no task is in flight, and hands over to review and ship by the task's mode.
- /rr-setup
Survey
Scaffolds
.ai/, surveys the code, asks you for the test commands, the weight table and the constitution. Never touches application code. - /rr-spec
Intent
For a feature: one question at a time, then requirements and design, checked against your principles.
- /rr-plan
Weigh
Names the files, recalls what the record knows about them, scores six axes and picks the mode.
- /rr-build
Build
Edits inside the task's files only. Runs each step's tests, then the suite once, to the end.
- /rr-review
Review
One adversarial reviewer at the mode's tier. Blockers are fixed as one batch.
- /rr-ship
Close
Measures the two facts, asks for your approval when the mode needs it, writes the commit body.
At any point:/rr-statuswhere the task stands, in twenty lines ·/rr-status --costwhat it cost, at zero model cost ·/rr-build-alla whole plan as one task
The ceremony scales with the blast radius.
A typo fix stays a one-line edit. A payment change gets a written plan, an adversarial review and your approval. Scope, reversibility and coverage are measured from the tree. Behaviour, domain and novelty are declared with evidence, and a declaration below a measurement is replaced by the measurement.
| Axis | 2 means | Measured by |
|---|---|---|
| scope | more than 5 referencing files, or a hot path | reference count, git grep |
| behaviour | changes a process: state, async, ordering | declared; keyword floor |
| reversibility | data, schema, infra, an external write | needs_step, irreversible globs |
| domain | money, tax, identity, personal data | module globs |
| novelty | a new mechanism or dependency | declared; manifest floor |
| coverage | no test: characterize first | a test named after each file |
/rr-build fix the typo in the checkout button label scope 0 1 reference behaviour 0 a label (declared) reversibility 0 git revert returns everything domain 1 templates/** is customer-visible novelty 0 nothing new (declared) coverage 1 no test names the file W 2 → light
| Mode | When | Plan | Review | You stop it at |
|---|---|---|---|---|
| light | W ≤ 2 | inline: files, test, rollback | only when a fact at close is red | nothing |
| guarded | W 3–7 | plan mode, which you accept | one pass on BALANCED | the plan |
| full | W ≥ 8 | written; architect on STRONG when the design is open | one pass on STRONG, security focus when domain = 2 | the plan, and the tree at close |
Reversibility or domain at 2 is full whatever W is. Both at 2, or DROP, TRUNCATE, DELETE FROM in the diff, is full_irreversible: you approve with approve --confirm <operation> in your own terminal. verify and close re-score the two hard axes from the real diff.
Guards, not guidelines.
In a signal box, the interlocking makes a conflicting route impossible to set. Here one Python hook sees every tool call in both runtimes and refuses the conflicting action, with the reason and the way out.
Git
ai-guard.pyForce push, remote branch delete, history rewrite, push or merge to a protected branch, gh pr merge, --no-verify, a staged secret, a deploy, npm publish.
Paths
ai-guard.py.env, secrets/, keys, dumps, production logs; writes to the task state, the journal, the policies' JSON and the installed guards.
Scope
ai-guard.pyAn edit to a file the step does not name: SCOPE_CHANGE_REQUIRED. Widen in place with start --amend --why, journaled.
Decision
ai-guard.py · dialog-gate.pyHolds every tool until you answer in the runtime's own dialog. Your pick runs once, verbatim; anything else is CONSENT_REQUIRED.
Large read
cap-large-read.pyAn unbounded Read over 4 000 lines or 250 KB is refused; a limit within budget passes.
Context
context-guard.pyWarns from 80% of the compaction point, snapshots before compaction, injects the handoff when a session starts.
Runtime
runtime-gate.pySends the top model's launches one tier down during an outage or at 90% of the weekly limit.
Record
memory-pre-push.pyReads the pushed commits before they leave: a malformed package, a lost record, a second seal or a SQLite file under .ai/ stops the push.
The guards are regex over a command line or a path: a tripwire for the honest mistake, not a security boundary. Branch protection, secrets off the developer machine and human review stay the real backstops. Read the known risks before relying on them.
Done is measured, not claimed.
state.py close asks no model whether the work is finished. It measures two facts on the tree as it is now. Green and green in light mode closes the task; guarded and full add the review and your stop.
The suite is green on this tree
The verification command runs once, to the end, with the log under .ai/state/logs/. Every failure is fixed as one batch. Two whole-suite runs are the budget; the third is your decision.
facts: (a) suite green on 3f9c2e1a7 (run 1)
Every changed file is in the task
The diff since the base tree is compared with the files the task named. One file outside, and close says which and stops.
(b) 2 changed files, all in the task's files
Pay for thinking, not for re-reading.
Prompts name tiers, not models. The installed profile maps each tier to a model for your plan and runtime. Readers run on the cheapest model; the expensive tiers run one at a time, on a named trigger.
Reading and running: Explore, log-reader.
Discovery with judgement, the guarded review, batch implementers.
The full review, the security review, architect in /rr-spec.
Only what STRONG could not settle. One agent at a time.
The session itself implements. /rr-status --cost reads the local transcripts of both runtimes and shows tokens and cost per task, session, model and tier, without calling any model.
One tree. Two runtimes. One record.
Start a task in Claude Code and finish it in Codex. The knowledge base, the policies, the task state and the compact record of every closed task live in .ai/, in the repository, next to the code they describe.
The task state
.ai/state/state.json and a journal a human can read. A guard keeps agents from writing either directly.
The record
On close, who, what, how and why become JSON lines in .ai/memory/, sealed and checked by a pre-push hook. recall hands an agent what the record says about the files it is about to touch.
The local index
Optional SQLite with full-text search. It is rebuilt from the files whenever they change and is never the only copy of anything.
How storage works →Project schema 11. A quieter update.
risk-tiers retired
Migration 0011 takes the project from schema 10 to 11 and retires .ai/policies/risk-tiers.*; weight.json and weight.md replace them.
A cleaner --update
/rr-setup --update renders weight.md from weight.json instead of merging it, and proposes the cleanup of files the plugin no longer ships.
Templates from the plugin
The plugin supplies the SDLC templates, so a project no longer carries its own copies.
CI in two tiers
A fast smoke suite on every pull request; the full suite nightly, on demand and on release tags.
Guard and state fixes
Fixes in the path guard and in state.py. Upgrade the plugin on every machine before a project is committed at schema 11.
Full list in the CHANGELOG.
Pin the version. Restart.
Node 18+ and python3. The installer detects Claude Code, Codex or both and proposes the plan it found. The hooks it installs see every command an agent runs, so pin the version. In Codex, open /hooks and trust the entries; until you do, nothing is enforced.
npx riskrails@3.1.0
# then, in any project /rr-setup # once /rr-build <what you want> # every change # options npx riskrails@3.1.0 --target codex # auto | claude | codex | both npx riskrails@3.1.0 --plan max # pro | team-pro | team-max | max | max20 npx riskrails@3.1.0 --dry-run # print, write nothing # coming from claude-agentic 2.x /rr-setup --update # dry run first; then --apply
Upgrading from 2.x renames the plugin, replaces the risk tiers with the weight and moves the task state. Follow the six steps in the README, in order.