riskrails 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/README.md +246 -812
- package/agents/ai-reviewer.md.tmpl +6 -4
- package/ai/policies/review-economy.md +7 -2
- package/ai/using-riskrails.md +15 -0
- package/bin/claude-1m +15 -14
- package/bin/riskrails.js +108 -32
- package/codex/hooks.json +25 -2
- package/hooks/AGENTS.md +2 -2
- package/hooks/CLAUDE.md +2 -2
- package/hooks/ai-guard.py +29 -8
- package/hooks/ai-path-guard-defaults.json +3 -1
- package/hooks/codex-hooks.json +25 -2
- package/hooks/context-guard.py +251 -35
- package/hooks/dialog-gate.py +105 -0
- package/hooks/guard/common.py +5 -0
- package/hooks/guard/decision.py +123 -0
- package/hooks/guard/dialog.py +598 -0
- package/hooks/guard/git.py +49 -3
- package/hooks/guard/paths.py +62 -3
- package/hooks/hooks.json +37 -2
- package/hooks/memory-pre-push.py +28 -0
- package/hooks/runtime-gate.py +59 -12
- package/hooks/update_notice.py +135 -0
- package/instructions/routing.md +4 -4
- package/instructions/stub.md +2 -2
- package/package.json +1 -1
- package/profiles/max.json +3 -1
- package/profiles/pro.json +3 -1
- package/scripts/merge-codex-config.py +134 -0
- package/scripts/resolve-profile.py +15 -3
- package/settings.common.json +39 -2
- package/skills/AGENTS.md +2 -2
- package/skills/CLAUDE.md +2 -2
- package/skills/ai-init/templates/.ai/AGENTS.md +2 -2
- package/skills/ai-init/templates/.ai/VERSION +1 -1
- package/skills/ai-init/templates/.ai/policies/dialog.json +1 -0
- package/skills/ai-init/templates/.ai/policies/safety.md +22 -2
- package/skills/ai-init/templates/.ai/policies/weight.json +1 -0
- package/skills/ai-init/templates/.ai/policies/weight.md +2 -2
- package/skills/ai-init/templates/.ai/state/README.md +9 -6
- package/skills/ai-init/templates/AGENTS.block.md +1 -1
- package/skills/ai-init/templates/CLAUDE.block.md +1 -1
- package/skills/ai-init/templates/GEMINI.block.md +1 -1
- package/skills/ai-init/templates/junie-guidelines.block.md +1 -1
- package/skills/ai-task/memory_compact.py +968 -0
- package/skills/ai-task/memory_format.py +775 -0
- package/skills/ai-task/memory_store.py +528 -0
- package/skills/ai-task/memory_sync.py +1571 -0
- package/skills/ai-task/sensors.py +2 -2
- package/skills/ai-task/state.py +967 -70
- package/skills/ai-task/weight.py +3 -3
- package/skills/project-init/templates/constitution.md +1 -1
- package/skills/project-update/history/blobs/164cd34c182de88c33de7df35beb29200d792419e1c13a5f0b2104f944e57be7 +1 -0
- package/skills/project-update/history/blobs/1d1d907449ec74594d0ee923ac142ae746b9aedf0a1eda9469f075a44d23e3d7 +13 -0
- package/skills/project-update/history/blobs/23a42febbacaa7b52a802d428033a99c0694391c81d51fe3b6e1dff608a641d5 +67 -0
- package/skills/project-update/history/blobs/5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 +49 -0
- package/skills/project-update/history/blobs/5f65bc7ab3fc3c4e75ae05d49eaf4331fafc398c661abe33c69f7380a720344c +32 -0
- package/skills/project-update/history/blobs/7be527cd648b27cca6e267de30f8e33a2ce17d5631a82694f23ae5c0623efe1f +15 -0
- package/skills/project-update/history/blobs/7ee99edafe7cec45ff577376c0d0178fdb2690134d4656488f46e129aba3cc5a +67 -0
- package/skills/project-update/history/blobs/8b3ef9586491a426767c01da5d01bdd6b0655c576c68c4cc6dfb093367817dc0 +67 -0
- package/skills/project-update/history/blobs/917df3320d778ddbaa5c5c7742bc4046bf803c36ed2b050f30844ed206783469 +1 -0
- package/skills/project-update/history/blobs/935af1019931058ec3a0b834fe230e127ec914f75edcc4a6c589cfe733b0b979 +13 -0
- package/skills/project-update/history/blobs/ae5ce131d6bae401199ff32d8c3dc4d8aec188010a9111624ac64506e2116ff4 +84 -0
- package/skills/project-update/history/blobs/ca4499d829268c8766fb3b825a515e88dc55c237e9d202816b50959b4dac031f +13 -0
- package/skills/project-update/history/blobs/e9adb7a9905eb719ac78ab96671f055da53e5001810f5ad6d6c94306b0323fe7 +74 -0
- package/skills/project-update/history/blobs/fae3064e648cdc55be4aeb1177525b302a5e34ebd752e4a904f47dc0f4557b96 +71 -0
- package/skills/project-update/history/index.json +32 -13
- package/skills/project-update/migrations/0009_state_file.py +25 -0
- package/skills/project-update/migrations/0010_dialog_switch.py +30 -0
- package/skills/project-update/migrations/README.md +2 -1
- package/skills/project-update/update.py +12 -3
- package/skills/rr-build/SKILL.md +14 -5
- package/skills/rr-build-all/SKILL.md +190 -0
- package/skills/rr-plan/SKILL.md +13 -3
- package/skills/rr-review/SKILL.md +18 -8
- package/skills/rr-ship/SKILL.md +16 -8
- package/skills/rr-status/SKILL.md +2 -2
- package/skills/usage-report/usage-report.py +41 -8
package/README.md
CHANGED
|
@@ -1,869 +1,303 @@
|
|
|
1
1
|
# RiskRails
|
|
2
2
|
|
|
3
|
-
[](
|
|
5
|
-
[](
|
|
3
|
+
[](https://github.com/vanssata/claude-agentic-sdlc-module/releases/tag/v3.0.1)
|
|
4
|
+
[](docs/routing.md)
|
|
5
|
+
[](docs/known-risks.md)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
8
|
**Let an AI agent work on your legacy production code without the agent
|
|
9
9
|
widening its own scope, reading a production secret, force-pushing or
|
|
10
10
|
deploying.**
|
|
11
11
|
|
|
12
|
-
**RiskRails** (`riskrails`) is a plugin for **Claude Code and Codex**, built for
|
|
13
|
-
with undocumented business rules, old workarounds and behaviour
|
|
14
|
-
rely on today.
|
|
15
|
-
|
|
12
|
+
**RiskRails** (`riskrails`) is a plugin for **Claude Code and Codex**, built for
|
|
13
|
+
codebases with undocumented business rules, old workarounds and behaviour
|
|
14
|
+
customers rely on today. Each change is **weighed** on six axes and runs in one
|
|
15
|
+
of three modes — light, guarded, full — so a typo fix stays a one-line edit and
|
|
16
|
+
a payment change gets a written plan, an adversarial review and a human's
|
|
17
|
+
approval. Risky actions are stopped by a hook, not by a polite request in a
|
|
18
|
+
prompt.
|
|
16
19
|
|
|
17
20
|
```bash
|
|
18
|
-
npx riskrails@3.0.
|
|
21
|
+
npx riskrails@3.0.1 # detects Claude Code, Codex or both (Node 18+, python3)
|
|
19
22
|
```
|
|
20
23
|
|
|
21
|
-
Then, in any project: `/
|
|
22
|
-
change.
|
|
23
|
-
|
|
24
|
-
Upgrading from `claude-agentic`: `npx riskrails` moves `<home>/claude-agentic/` to
|
|
25
|
-
`<home>/riskrails/` and leaves a symlink at the old path for one version, and
|
|
26
|
-
`/rr-setup --update` renames each project's managed-block markers (migration
|
|
27
|
-
0008). The task state stays in `.ai/state/current.json`; a later release moves
|
|
28
|
-
it to `state.json`, and this one already reads either.
|
|
24
|
+
Then, in any project: `/rr-setup` once, and `/rr-build <what you want>` for
|
|
25
|
+
every change.
|
|
29
26
|
|
|
30
27
|
**What you get**
|
|
31
28
|
|
|
32
|
-
- **Guards, not guidelines.**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
- **Guards, not guidelines.** One hook on every tool call refuses an edit
|
|
30
|
+
outside the task's files, a read of a production secret, a force push, a
|
|
31
|
+
push to a protected branch, a deploy command, `npm publish`. No agent merges
|
|
32
|
+
or deploys; a commit, a push or an approval runs only as the command you
|
|
33
|
+
picked in the runtime's own dialog, verbatim, once — or you run it yourself.
|
|
34
|
+
- **Weight, not ceremony.** Scope, reversibility and coverage are measured
|
|
35
|
+
from the tree; behaviour, domain and novelty are declared with evidence. The
|
|
36
|
+
sum picks the mode, the mode picks who plans, who reviews and whether you
|
|
37
|
+
sign off. The mode only rises during a task; lowering it is yours.
|
|
38
|
+
- **Deterministic gates.** `state.py` measures and refuses; no model decides
|
|
39
|
+
whether a step stayed in scope, whether the suite is green on this tree, or
|
|
40
|
+
whether a budget is spent. Every decision is in a journal a human can read.
|
|
41
|
+
- **Cheap by default.** Readers on the cheapest model, the session on the one
|
|
42
|
+
that thinks, the top tier only on a named trigger, one short always-loaded
|
|
43
|
+
file per runtime. `/rr-status --cost` shows where the tokens went, at zero
|
|
44
|
+
model cost.
|
|
42
45
|
- **One `.ai/` tree for both runtimes.** Start a task in Claude Code and finish
|
|
43
|
-
it in Codex; the knowledge base, policies
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
there or in the session, and `state.py questions --sync` puts the answer back
|
|
105
|
-
into the state. The journal is what `/ai-status` and `/usage-report` read, at
|
|
106
|
-
zero model cost — no model is involved in writing or reading any of these files.
|
|
46
|
+
it in Codex; the knowledge base, the policies, the task state and the compact
|
|
47
|
+
record of every closed task are shared.
|
|
48
|
+
|
|
49
|
+
Tuned for **one developer who knows the codebase**, on a Claude Pro, Max or
|
|
50
|
+
Team plan, or a ChatGPT Plus or Pro plan, following the
|
|
51
|
+
[AI-native SDLC playbook](https://claude.com/blog/the-ai-native-sdlc-playbook).
|
|
52
|
+
|
|
53
|
+
**Version 3.0.1.** The guards have limits: read [Known risks](docs/known-risks.md)
|
|
54
|
+
before relying on them. What is decided but not built is in [ROADMAP.md](ROADMAP.md).
|
|
55
|
+
|
|
56
|
+
## Upgrade to 3.0
|
|
57
|
+
|
|
58
|
+
3.0 renames the plugin from `claude-agentic` to `riskrails`, replaces the risk
|
|
59
|
+
tiers with the weight and the three modes, replaces the `/ai-*` and `/sdlc-*`
|
|
60
|
+
commands with `/rr-*`, ports the guards to one Python process, and moves the
|
|
61
|
+
task state to `.ai/state/state.json`. The old names stay as aliases for this
|
|
62
|
+
version. Step by step, in this order:
|
|
63
|
+
|
|
64
|
+
1. **Commit every project's `.ai/`** before anything else; the update tells you
|
|
65
|
+
when the tree is dirty, and `git restore` is the rollback.
|
|
66
|
+
2. **Close or abandon tasks in flight.** A task started by 2.x is refused by
|
|
67
|
+
the new commands as `old surface`; finish it with the version that started
|
|
68
|
+
it, or drop it with `python3 ~/.claude/skills/ai-task/state.py close
|
|
69
|
+
--abandon`. Check with `/rr-status`.
|
|
70
|
+
3. **Update the home** on every machine, for every runtime:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx riskrails@3.0.1 # or: npx -y riskrails@latest --yes
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
It moves `<home>/claude-agentic/` to `<home>/riskrails/` and leaves a
|
|
77
|
+
symlink at the old path for one version, replaces the old hook entries with
|
|
78
|
+
`ai-guard.py` and `runtime-gate.py`, retires the old agents and shim files
|
|
79
|
+
to `backups/<date>/`, and rewrites the managed block. Restart the runtime.
|
|
80
|
+
In Codex, open `/hooks` and **trust the changed entries** — until you do,
|
|
81
|
+
nothing is enforced. `--dry-run` shows all of it first.
|
|
82
|
+
4. **Update each project:**
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
/rr-setup --update # dry run first; read it; then --apply
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Four migrations run inside the dry run, in order, before the merge:
|
|
89
|
+
`0007` creates `.ai/policies/weight.json`, carrying over the project's
|
|
90
|
+
edits to its risk-tier table; `0008` renames the managed-block markers and
|
|
91
|
+
the router's routes; `0009` moves `current.json` to `state.json`; `0010`
|
|
92
|
+
creates `.ai/policies/dialog.json` with the dialog on. Edited markdown is
|
|
93
|
+
three-way merged, JSON policies key by key with a confirmation per key;
|
|
94
|
+
`delete?` proposals are left for you to confirm. The schema becomes 10.
|
|
95
|
+
5. **Decide two things the migrations cannot.** Keep the dialog (then amend
|
|
96
|
+
constitution C5 as `0010` hints) or set `"enabled": false`; and tune the
|
|
97
|
+
weight table — `domain.modules`, `customer_visible`, `hot_paths`,
|
|
98
|
+
`needs_step`, `irreversible` — then `state.py weight --mirror`.
|
|
99
|
+
6. **Verify**: `/rr-status` shows schema 10 and no task in flight; a
|
|
100
|
+
`/rr-build` on a small change runs light and closes on the two facts.
|
|
101
|
+
Commit `.ai/`.
|
|
102
|
+
|
|
103
|
+
Every machine of a team upgrades the plugin before a project at schema 10 is
|
|
104
|
+
committed; an older plugin refuses such a project. Rollback and the full
|
|
105
|
+
migration table are in [docs/project-update.md](docs/project-update.md); the
|
|
106
|
+
home-side details in [docs/installation.md](docs/installation.md).
|
|
107
107
|
|
|
108
108
|
## Install
|
|
109
109
|
|
|
110
|
-
Pin the version: `npx riskrails` alone runs whatever was published last, and the
|
|
111
|
-
hooks it installs see every command an agent runs.
|
|
112
|
-
|
|
113
110
|
```bash
|
|
114
|
-
npx riskrails@3.0.
|
|
115
|
-
npx riskrails@3.0.
|
|
116
|
-
npx riskrails@3.0.
|
|
117
|
-
npx riskrails@3.0.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# Claude-side options. The plan is detected from ~/.claude.json (organizationRateLimitTier
|
|
122
|
-
# tells Max 5x from 20x) and, on a terminal, proposed for you to confirm with Enter (--yes
|
|
123
|
-
# takes the detection without asking):
|
|
124
|
-
npx riskrails@3.0.0 --plan pro # Pro: opusplan session, opus pinned for EXPERT
|
|
125
|
-
npx riskrails@3.0.0 --plan team-pro # Team, Standard seat: the pro profile with the Team label
|
|
126
|
-
npx riskrails@3.0.0 --plan team-max # Team, Premium seat: the max profile, Fable on architect
|
|
127
|
-
npx riskrails@3.0.0 --plan max # Max 5x: Opus 5.5 [1m] session (1M window by default), Fable only on architect
|
|
128
|
-
npx riskrails@3.0.0 --plan max20 # Max 20x: max's settings, larger budgets (6 agents, EXPERT without asking)
|
|
129
|
-
npx riskrails@3.0.0 --plan max --fable no # no Fable anywhere; architect inherits the Opus session
|
|
130
|
-
npx riskrails@3.0.0 --plan balanced-max # BalancedMax: only the session is strong (Opus 5.5, 200k); readers on Haiku,
|
|
131
|
-
# judgement on Sonnet, reviews on Opus, EXPERT at high, no Fable, no xhigh;
|
|
132
|
-
# also picks the Codex balanced-max profile (Sol session, readers on Luna)
|
|
133
|
-
|
|
134
|
-
# Codex-side options (the ChatGPT plan is auto-detected from ~/.codex/auth.json):
|
|
135
|
-
npx riskrails@3.0.0 --codex-plan plus # Plus: Sol at medium, three agent threads, Astra at high, no xhigh
|
|
136
|
-
npx riskrails@3.0.0 --codex-plan pro # Pro: Sol at high, six agent threads, Astra at xhigh for ai-expert
|
|
137
|
-
npx riskrails@3.0.0 --codex-plan balanced-max # Pro's budgets, Sol session, FAST readers on Luna, Astra at high
|
|
111
|
+
npx riskrails@3.0.1 # detects Claude Code, Codex, or both; proposes the detected plan
|
|
112
|
+
npx riskrails@3.0.1 --target codex # auto | claude | codex | both
|
|
113
|
+
npx riskrails@3.0.1 --plan max --fable no # pro | team-pro | team-max | max | max20 | balanced-max
|
|
114
|
+
npx riskrails@3.0.1 --codex-plan plus # plus | pro | balanced-max
|
|
115
|
+
npx riskrails@3.0.1 --context economy # the plan's compaction window; the context guard also blocks
|
|
116
|
+
npx riskrails@3.0.1 --dry-run # print what would be written, write nothing
|
|
117
|
+
npx -y riskrails@latest --yes # update in place, keeping the recorded plan
|
|
138
118
|
```
|
|
139
119
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
installed and enabled (Claude Code: `installed_plugins.json` at user scope and
|
|
148
|
-
`enabledPlugins`; Codex: `[plugins."riskrails@<marketplace>"] enabled = true`
|
|
149
|
-
in `config.toml`), `npx` registers none of the plugin's hooks and installs the
|
|
150
|
-
skills without their `SKILL.md`, so nothing loads or runs twice and the skills
|
|
151
|
-
still find their scripts under `~/.claude/skills/`. A plugin cache folder by
|
|
152
|
-
itself changes nothing: `npx` installs everything. With
|
|
153
|
-
the plugin alone there is no `ai-reviewer` agent, so a task whose mode needs a
|
|
154
|
-
review has nothing to run it with.
|
|
155
|
-
|
|
156
|
-
`--target auto` installs for each runtime whose CLI is on `PATH`, and asks for
|
|
157
|
-
that runtime's plan only. A `~/.claude` or `~/.codex` directory without its CLI
|
|
158
|
-
is skipped with a one-line note — `--target both` includes it anyway, which is
|
|
159
|
-
also the way in when the CLI exists but is not on this shell's `PATH` (an IDE
|
|
160
|
-
extension's bundled binary, `sudo` without `~/.local/bin`). If it
|
|
161
|
-
finds neither CLI it stops and tells you which `--target` to name; it never
|
|
162
|
-
guesses one.
|
|
163
|
-
|
|
164
|
-
Restart the runtime afterwards.
|
|
165
|
-
|
|
166
|
-
- **Claude Code**: `/skills` should list `ai-init`, `ai-audit`, `ai-task` and
|
|
167
|
-
`ai-status`; `/hooks` should list `ai-guard.py`, the one guard command.
|
|
168
|
-
- **Codex**: `/hooks` shows the hooks — **and you must review and trust them
|
|
169
|
-
there before they run.** Codex does not execute a non-managed hook until it has
|
|
170
|
-
been trusted, so until you do, nothing is being enforced.
|
|
171
|
-
|
|
172
|
-
Re-running the installer updates in place: it backs up what it replaces, never
|
|
173
|
-
duplicates a hook entry, and never overwrites your edits to `ai-git-guard.json`.
|
|
174
|
-
|
|
175
|
-
### What it installs
|
|
176
|
-
|
|
177
|
-
| Source | Claude target | Codex target |
|
|
178
|
-
|---|---|---|
|
|
179
|
-
| `profiles/{pro,max}.json` + `settings.common.json` | deep-merged into `settings.json` | — |
|
|
180
|
-
| `profiles/codex-{plus,pro}.json` | — | six managed keys in `config.toml` |
|
|
181
|
-
| `instructions/stub.md` | a managed block in `~/.claude/CLAUDE.md` | a managed block in `~/.codex/AGENTS.md` |
|
|
182
|
-
| `instructions/routing.md` | `~/.claude/riskrails/routing.md`, read on demand | `~/.codex/riskrails/routing.md`, read on demand |
|
|
183
|
-
| `agents/*.md.tmpl` | rendered to `~/.claude/agents/*.md`, each tier's model and effort from the plan (`ai-expert` and `architect` have their own model line) | rendered to `~/.codex/agents/*.toml`, the model pinned per role |
|
|
184
|
-
| `profiles/*.json` | the plan's settings, and its tier and budget tables (`riskrails`), resolved by `scripts/resolve-profile.py` into `~/.claude/riskrails/profile.json` | the same, into `~/.codex/riskrails/profile.json` |
|
|
185
|
-
| `hooks/*` | `~/.claude/hooks/` — six hooks, `runtime-gate` (the old `fable-gate`/`codex-model-gate` names are shims), the shared library and the guards' default config | `~/.codex/hooks/` + `codex/hooks.json` |
|
|
186
|
-
| `skills/*/` | `~/.claude/skills/` | `~/.codex/skills/` |
|
|
187
|
-
|
|
188
|
-
The skills are `/ai-init`, `/ai-audit`, `/ai-task`, `/ai-status`, `/project-init`,
|
|
189
|
-
`/project-update`, `/sdlc-intent`, `/sdlc-spec`, `/sdlc-plan` and
|
|
190
|
-
`/usage-report`, identical in both runtimes.
|
|
191
|
-
|
|
192
|
-
The Codex install is a strict subset in one place: `cap-large-read.py` is not
|
|
193
|
-
installed there, because Codex's read tool is not on the hook path. The read
|
|
194
|
-
rule is still written into `AGENTS.md`; it is just not mechanical there.
|
|
195
|
-
`context-guard.py` **is** installed on both — Codex has `UserPromptSubmit`,
|
|
196
|
-
`PreCompact` and `SessionStart` too, so the handoff, the pending questions and
|
|
197
|
-
`session.json` cross unchanged. What does not cross is the transcript-derived
|
|
198
|
-
snapshot: it is built from a Claude transcript and stays Claude-only. One file
|
|
199
|
-
serves both; it reads its own location to know which runtime it is in.
|
|
200
|
-
|
|
201
|
-
`profiles/codex-{plus,pro}.json` is the machine-readable routing contract, one
|
|
202
|
-
file per ChatGPT plan — session model and effort, thread cap,
|
|
203
|
-
`[agents]` defaults, the four tiers and every role's tier and sandbox mode. Both
|
|
204
|
-
the agent renderer and the config merge read it, so there is one place to change
|
|
205
|
-
a Codex routing decision.
|
|
206
|
-
|
|
207
|
-
### What loads on every turn
|
|
208
|
-
|
|
209
|
-
Context length is the running cost: an always-loaded instruction file is re-read
|
|
210
|
-
on every turn of every task, whether it is relevant or not. So the plugin keeps
|
|
211
|
-
exactly one always-loaded file per runtime, and it is short.
|
|
212
|
-
|
|
213
|
-
| Where | What it is | Budget |
|
|
214
|
-
|---|---|---|
|
|
215
|
-
| `~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md` | the managed block: the rules an agent is unsafe without | 2 560 B |
|
|
216
|
-
| a project's `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.junie/guidelines.md` | the same block, project scope | 2 048 B |
|
|
217
|
-
| `~/.claude/riskrails/routing.md` | the model ladder, the context-guard thresholds, the guard list — read when a routing question comes up | — |
|
|
218
|
-
| a project's `.ai/AGENTS.md` | a router: one row per job, pointing at the policy that answers it | advisory 4 096 B |
|
|
219
|
-
| `docs/sdlc/constitution.md` | the 10–15 principles the project does not negotiate, cited as `C<n>` by `/sdlc-spec` and the planner | 15 lines, 4 096 B |
|
|
220
|
-
| `.ai/rules/<slug>.md` | a rule scoped to the directories it names, rendered into their instruction files by `/project-update` | — |
|
|
221
|
-
|
|
222
|
-
All of the block text comes from one source, `instructions/stub.md`, rendered per
|
|
223
|
-
scope and runtime by `skills/project-update/render_instructions.py`; the committed
|
|
224
|
-
templates are build artefacts and `render_instructions.py build --check` fails the
|
|
225
|
-
suite on drift. `npx riskrails` prints what the block costs after every install
|
|
226
|
-
(`managed block 1 903 B (budget 2 560), file 8 457 B — the rest is yours`) and
|
|
227
|
-
never touches the rest of the file; `/ai-status` prints the same line for a
|
|
228
|
-
project. Downstream the budget is advisory — the plugin measures, it does not
|
|
229
|
-
refuse a file it does not own.
|
|
230
|
-
|
|
231
|
-
### Editing `config.toml` safely
|
|
232
|
-
|
|
233
|
-
There is no comment-preserving TOML writer in the standard library, so
|
|
234
|
-
`scripts/merge-codex-config.py` edits the six managed keys line by line, then
|
|
235
|
-
parses the file before and after and refuses to write unless the *only* keys that
|
|
236
|
-
differ are the six it manages. Your comments, `[projects.*]`, `[mcp_servers.*]`,
|
|
237
|
-
`[marketplaces.*]` and every other setting survive; a malformed file aborts the
|
|
238
|
-
merge and is left untouched. The previous version is kept as `config.toml.bak`.
|
|
239
|
-
|
|
240
|
-
**One accepted change to your defaults:** the Codex session is set to Sol at
|
|
241
|
-
`high`, not Astra. Astra stays available as the EXPERT escalation. This is the
|
|
242
|
-
whole point of the routing — the session model is the largest single cost, and
|
|
243
|
-
it should not be the most expensive model by default.
|
|
244
|
-
|
|
245
|
-
### Limits it sets
|
|
246
|
-
|
|
247
|
-
| Limit | Value | Default |
|
|
248
|
-
|---|---|---|
|
|
249
|
-
| `bashOutputMaxChars` (Claude) | 75 000 | 30 000 |
|
|
250
|
-
| `taskOutputMaxChars` (Claude) | 80 000 | — |
|
|
251
|
-
| `MAX_MCP_OUTPUT_TOKENS` (Claude) | 40 000 | 25 000 |
|
|
252
|
-
| `cap-large-read.py` (Claude) | refuses an unbounded `Read` over 4 000 lines or 250 KB | no limit |
|
|
253
|
-
| `autoCompactWindow` (Claude) | 800 000 on Max and Team Max, 300 000 on Pro and Team Pro. Claude Code caps it at the model's own window, so one setting means compaction near 167k on a 200k model and near 767k on a `[1m]` one; on Pro the cap decides, at 167k | the model window |
|
|
254
|
-
| `claude-1m [opus\|fable]` (Claude, Max and Team Max) | pins one session to `opus[1m]`, or to `fable[1m]` (Fable 5.1), at launch. The large window comes from the per-model cap, not from the launcher, so `/model opus[1m]` reaches it too; `CLAUDE_1M_COMPACT_WINDOW` exports `CLAUDE_CODE_AUTO_COMPACT_WINDOW` for that process alone to compact *earlier* than 767k | `/model` only, and the same cap applies |
|
|
255
|
-
| `context-guard.py` (both runtimes) | warns from 80% of the point where compaction fires and holds a prompt back once from 120% — 133k and 200k on a 200k Max session, 613k and 920k on a `[1m]` one; `AI_CONTEXT_WARN_TOKENS` / `AI_CONTEXT_BLOCK_TOKENS` set them in tokens, `0` turns one off | no guard |
|
|
256
|
-
| `model` (Claude) | `opusplan` on Pro and Team Pro: Opus in plan mode, Sonnet when executing; `claude-opus-5-5[1m]` (Opus 5.5, 1M window) by default on Max, Max 20x and Team Max, `opus` (200k) per task through `/model`; `claude-opus-5-5` (200k) on BalancedMax | Sonnet 5 on Pro |
|
|
257
|
-
| `effortLevel` (Claude) | `medium` on both plans; agents raise it per task | — |
|
|
258
|
-
| `model` (Codex) | `gpt-5.6-sol` at `high` on Pro, `medium` on Plus; subagents `gpt-5.6-terra` at `medium` | — |
|
|
259
|
-
| `max_concurrent_threads_per_session` (Codex) | 6 on Pro, 3 on Plus | runtime default |
|
|
260
|
-
| `ai-expert` effort (Codex) | `xhigh` on Pro, `high` on Plus; `xhigh` is never used on Plus | — |
|
|
261
|
-
|
|
262
|
-
The Read guard is a guardrail, not a cage: an explicit `limit` always goes
|
|
263
|
-
through, so reading something large stays possible but has to be deliberate.
|
|
120
|
+
Pin the version: the hooks it installs see every command an agent runs. It
|
|
121
|
+
installs the managed block, the plan's settings and limits, the five agents
|
|
122
|
+
with their tiers baked in, the hooks, the skills, the shared tree at
|
|
123
|
+
`<home>/riskrails/ai/` and the resolved profile; re-running it updates in
|
|
124
|
+
place with dated backups. The plugin route (`/plugin install riskrails`)
|
|
125
|
+
carries the skills and hooks only — run `npx` as well. Every option, every
|
|
126
|
+
limit it sets and what loads on every turn: [docs/installation.md](docs/installation.md).
|
|
264
127
|
|
|
265
128
|
## Use
|
|
266
129
|
|
|
267
130
|
The same commands in both runtimes:
|
|
268
131
|
|
|
269
|
-
|
|
270
|
-
/ai-init # in a project: survey it, build .ai/ and docs/sdlc/
|
|
271
|
-
/ai-task <what you want> # run one change through the pipeline
|
|
272
|
-
/ai-status # where does the current task stand, on which models
|
|
273
|
-
/ai-audit # score the repo against the twelve AI-SDLC plays
|
|
274
|
-
/usage-report # what it cost, from local transcripts, at zero model cost
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### Updating a project after the plugin changes
|
|
278
|
-
|
|
279
|
-
`npx riskrails` updates `~/.claude/` (or `~/.codex/`), and with it the shared
|
|
280
|
-
tree at `<home>/riskrails/ai/`: the 29 policies, agent contracts,
|
|
281
|
-
workflows and templates that are the same in every project. A project does not
|
|
282
|
-
copy them; it reads them from there, and a file at the same path under `.ai/`
|
|
283
|
-
wins as an **override**. `<home>/riskrails/ai-path <path>` prints which one
|
|
284
|
-
applies. What a project owns stays in the project — `.ai/project/`, `rules/`,
|
|
285
|
-
`state/`, `reports/`, `VERSION`, `AGENTS.md`, and the policies `risk-tiers.json`,
|
|
286
|
-
`risk-tiers.md`, `path-guard.json`, `testing.md`, `coding.md`, `safety.md` and
|
|
287
|
-
`security.md` — together with `docs/sdlc/` and the `CLAUDE.md` block. Those are
|
|
288
|
-
copies, and stay as they were. Pull the new ones in with:
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
/project-update # dry run, confirm the policy changes, apply, merge conflicts
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Running `/ai-init` or `/project-init` again in an initialised project does the
|
|
295
|
-
same. `/ai-status` says when a project is behind.
|
|
296
|
-
|
|
297
|
-
The plugin ships every version of every project-owned template it has ever
|
|
298
|
-
installed (`skills/project-update/history/`, built from git by
|
|
299
|
-
`tools/build-template-history.py`; the shared files keep only their hashes,
|
|
300
|
-
since nothing is merged into them any more). That is how the update tells files
|
|
301
|
-
apart:
|
|
302
|
-
|
|
303
|
-
| The project file is | What happens |
|
|
132
|
+
| You want to | Run |
|
|
304
133
|
|---|---|
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
|
|
|
308
|
-
|
|
|
309
|
-
|
|
|
310
|
-
|
|
|
311
|
-
|
|
|
312
|
-
|
|
|
313
|
-
|
|
|
314
|
-
|
|
|
315
|
-
|
|
316
|
-
The
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
project
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
Codex. `/ai-status` says when a structure is
|
|
374
|
-
waiting, or came back after an adopt.
|
|
375
|
-
|
|
376
|
-
For work that needs a written intent and specification before any code:
|
|
134
|
+
| set up `.ai/` in a project, or bring it up to the installed plugin | `/rr-setup`, `/rr-setup --update` |
|
|
135
|
+
| move another AI tool's files, or the project's own toolbox, into this layout | `/rr-setup --adopt` |
|
|
136
|
+
| score the repository against the twelve plays | `/rr-setup --audit` |
|
|
137
|
+
| turn an idea into intent, requirements and design | `/rr-spec <idea>` |
|
|
138
|
+
| plan a spec into tasks, or one task into a mode and a start | `/rr-plan <spec or task>` |
|
|
139
|
+
| do the work, or resume it | `/rr-build <request>` (no task in flight: it runs `/rr-plan` first) |
|
|
140
|
+
| run a whole plan as one task | `/rr-build-all docs/sdlc/plans/<slug>.md` |
|
|
141
|
+
| review the change | `/rr-review` — guarded and full, or when `close` asks |
|
|
142
|
+
| close the task, get the human's approval | `/rr-ship` |
|
|
143
|
+
| see where the task stands, or what it cost | `/rr-status`, `/rr-status --cost` |
|
|
144
|
+
|
|
145
|
+
The old names — `/ai-init`, `/ai-task`, `/ai-status`, `/ai-audit`,
|
|
146
|
+
`/project-init`, `/project-update`, `/sdlc-intent`, `/sdlc-spec`,
|
|
147
|
+
`/sdlc-plan`, `/usage-report` — are aliases for one version.
|
|
148
|
+
|
|
149
|
+
### Three modes
|
|
150
|
+
|
|
151
|
+
`state.py weight` scores six axes 0–2 — scope, behaviour, reversibility,
|
|
152
|
+
domain, novelty, coverage — and the sum W picks the mode. The table is the
|
|
153
|
+
project's `.ai/policies/weight.json`; reversibility or domain at 2 is full
|
|
154
|
+
whatever W is ([docs/weight.md](docs/weight.md)).
|
|
155
|
+
|
|
156
|
+
| Mode | When | Plan | Review | Human stops |
|
|
157
|
+
|---|---|---|---|---|
|
|
158
|
+
| **light** | W ≤ 2 | inline: files, test, rollback | only when a fact at close is red | none |
|
|
159
|
+
| **guarded** | W 3–7 | plan mode, which you accept | one pass on BALANCED | the plan |
|
|
160
|
+
| **full** | W ≥ 8, or reversibility or domain = 2 | 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 |
|
|
161
|
+
|
|
162
|
+
A `full_irreversible` task (both hard axes at 2, or `DROP`, `TRUNCATE`,
|
|
163
|
+
`DELETE FROM` in the diff) is approved with `approve --confirm <operation>` in
|
|
164
|
+
your own terminal. `verify` and `close` re-score the two hard axes from the
|
|
165
|
+
real diff; a mode never drops during a task.
|
|
166
|
+
|
|
167
|
+
### The two facts
|
|
168
|
+
|
|
169
|
+
`close` measures, never asks: (a) a green whole-suite `verify` on the tree as
|
|
170
|
+
it is now; (b) every changed file inside the task's files. Green and green in
|
|
171
|
+
light mode closes the task. Tests run once, to the end, with the log under
|
|
172
|
+
`.ai/state/logs/`, and every failure is fixed as one batch; two whole-suite
|
|
173
|
+
runs are the budget, and the third is your decision.
|
|
174
|
+
|
|
175
|
+
### The dialog
|
|
176
|
+
|
|
177
|
+
With `.ai/policies/dialog.json` on (the default), the approval at close, a
|
|
178
|
+
budget refusal and a guarded `git commit` or `git push` come to you as a
|
|
179
|
+
question in the runtime's own dialog. The pick is recorded by a hook and the
|
|
180
|
+
command runs once, verbatim; a command that is not the pick is refused as
|
|
181
|
+
`CONSENT_REQUIRED`. Off, the same decisions are commands you run in your own
|
|
182
|
+
terminal.
|
|
183
|
+
|
|
184
|
+
### Batch — one task for a whole plan
|
|
185
|
+
|
|
186
|
+
`/rr-build-all docs/sdlc/plans/<slug>.md` runs every row of a programme plan as
|
|
187
|
+
the steps of one task: the ready rows go to parallel implementers on
|
|
188
|
+
BALANCED (a step's `after` serializes the dependent ones), the suite runs once,
|
|
189
|
+
every failure is fixed by parallel fixers as one batch, one review at the
|
|
190
|
+
mode's tier, its findings fixed as one batch, then tests and the review again
|
|
191
|
+
until it passes. After three failed review passes the next verdict is yours.
|
|
192
|
+
|
|
193
|
+
### The record
|
|
194
|
+
|
|
195
|
+
A closed task's who, what, how and why travel as a compact record: `state.py
|
|
196
|
+
memory enable` (yours, in a terminal) switches a project to packages mode, in
|
|
197
|
+
which `close` appends the task's entries to a per-developer batch under
|
|
198
|
+
`.ai/memory/` and a pre-push hook validates them; `state.py recall --files …`
|
|
199
|
+
gives an agent what the record holds about the files it is about to touch,
|
|
200
|
+
within a token budget. Task ids are `T-YYYY-MM-DD-HHMM-<slug>`
|
|
201
|
+
([docs/local-memory.md](docs/local-memory.md)).
|
|
377
202
|
|
|
378
|
-
|
|
379
|
-
/sdlc-intent <topic> # docs/sdlc/intent/<slug>.md
|
|
380
|
-
/sdlc-spec docs/sdlc/intent/<slug>.md # docs/sdlc/specs/<slug>.md
|
|
381
|
-
/sdlc-plan docs/sdlc/specs/<slug>.md # docs/sdlc/plans/<slug>.md
|
|
382
|
-
/ai-task build the plan in docs/sdlc/plans/<slug>.md
|
|
383
|
-
```
|
|
203
|
+
## The guards
|
|
384
204
|
|
|
385
|
-
|
|
386
|
-
want the agentic pipeline. Both scaffolds take
|
|
387
|
-
`--runtime auto|claude|codex|gemini|junie|both` — or a comma list — and default
|
|
388
|
-
to what the project already declares: `CLAUDE.md`/`.claude/`, `AGENTS.md`/`.codex/`,
|
|
389
|
-
`GEMINI.md`/`.gemini/`, `.junie/`. Gemini and Junie get the rendered project stub
|
|
390
|
-
only; they are not full runtimes here.
|
|
391
|
-
|
|
392
|
-
`/ai-init` reads the codebase and writes `.ai/`. It does not touch application
|
|
393
|
-
code — not a rename, not a formatting fix. Problems it finds are documented in
|
|
394
|
-
`.ai/project/known-risks.md`, not fixed. By default it runs a **light** survey:
|
|
395
|
-
it asks you for what you know, confirms it with `grep`, and sends only two
|
|
396
|
-
discovery agents to the places memory is least reliable — legacy and risks,
|
|
397
|
-
tests and data. `/ai-init --survey full` fans out six agents for a codebase new
|
|
398
|
-
to you. Either way it ends with the test commands written into
|
|
399
|
-
`.ai/policies/testing.md` — the scoped one a step runs, the full verification
|
|
400
|
-
command, and the e2e suite — which every later task runs before reporting
|
|
401
|
-
done.
|
|
402
|
-
|
|
403
|
-
The tooling policy, `policies/tooling.md` (shared; a project overrides it in
|
|
404
|
-
`.ai/`), says which MCP servers this repository enables for every task and why, which stay off, and how a single task asks for
|
|
405
|
-
one. The default is none. A connected server is in the system prompt of every
|
|
406
|
-
turn whether a task calls it or not, so the scaffolded `.claude/settings.json`
|
|
407
|
-
ships with `enableAllProjectMcpServers: false` and an empty
|
|
408
|
-
`enabledMcpjsonServers`. The same file holds the reading rule: a file too large
|
|
409
|
-
to open is read by the cheapest model — `Explore` for code, `log-reader` for
|
|
410
|
-
logs — which hands back the matching ranges with `file:line`, never the file.
|
|
411
|
-
|
|
412
|
-
## Risk tiers
|
|
413
|
-
|
|
414
|
-
Every task is classified before it is planned. The tier decides who plans it, who
|
|
415
|
-
reviews it, and whether a human signs it off. It does not depend on the runtime,
|
|
416
|
-
and it comes from the **scope** of the change — a process, a migration, money,
|
|
417
|
-
identity, infrastructure — never from how many files or lines it touches.
|
|
418
|
-
|
|
419
|
-
| Tier | Covers | Plan review | Adversarial review | Security review | Human approval |
|
|
420
|
-
|---|---|---|---|---|---|
|
|
421
|
-
| T0 | documentation, comments | no | no | no | no |
|
|
422
|
-
| T1 | formatting, a label, a template tweak | no | no | no | no |
|
|
423
|
-
| T2 | an isolated screen or form, a new field, a small feature that changes no process | no | the sensors; a model only when one is red | no | no |
|
|
424
|
-
| T3 | CRUD and presentation: a new resource with its screens, a non-critical API extension | no | the sensors; a model only when one is red | no | no |
|
|
425
|
-
| T4 | shared domain behaviour and processes: orders, workflows, async, integrations, an additive migration, config | the human, in plan mode | yes, STRONG | when auth or personal data | yes |
|
|
426
|
-
| T5 | payments, accounting, tax, fiscal, auth, order state, customer data | yes | yes | yes | yes |
|
|
427
|
-
| T6 | migration strategy, infrastructure, production architecture, destructive or data migrations | yes | yes | yes | yes |
|
|
428
|
-
|
|
429
|
-
The machine-readable source of truth is `.ai/policies/risk-tiers.json`, which
|
|
430
|
-
`/ai-init` copies into each project so the tiers can be tuned to that codebase.
|
|
431
|
-
|
|
432
|
-
### Who does each stage: the pipeline profile
|
|
433
|
-
|
|
434
|
-
The tier decides *whether* a stage runs; `pipeline_profile` in the same file
|
|
435
|
-
decides *who* runs it. The default is `solo`, which has two modes: **direct**
|
|
436
|
-
for T0–T4 — no pipeline ceremony, cheap readers only, a `sonnet` review at T2
|
|
437
|
-
or T3 only when a sensor is red, one `opus` review at T4 whose plan the human
|
|
438
|
-
approves in plan mode — and **sdlc** for T5–T6, the full pipeline:
|
|
439
|
-
|
|
440
|
-
| Tier | `solo` delegates to a subagent | `team` delegates |
|
|
205
|
+
| Hook | Where | Does |
|
|
441
206
|
|---|---|---|
|
|
442
|
-
|
|
|
443
|
-
|
|
|
444
|
-
|
|
|
445
|
-
|
|
|
446
|
-
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
`
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
then on, and only a human, in their own terminal, ever lowers a tier.
|
|
457
|
-
`state.py test-run` owns the test runs, keeps their output in a file and caps
|
|
458
|
-
how many there may be. `state.py review-gate` runs the sensor set — the
|
|
459
|
-
recorded suite result, the project's own linter and type checker, the diff, the
|
|
460
|
-
re-score, step-to-test traceability, repeated blocks, and whether the named
|
|
461
|
-
tests **fail without the change** — and up to T3, with every one of them
|
|
462
|
-
green, records `review_status: skipped_green`: the sensors are the review. A
|
|
463
|
-
tool nobody wrote down is `unavailable`, not green, and keeps the review; so
|
|
464
|
-
does a task over its diff budget, and so does a change into `config/`, a
|
|
465
|
-
dependency manifest or a migration, whose floor is T4. From T4 the review
|
|
466
|
-
always runs, with the sensor rows already in the ledger so the reviewer does
|
|
467
|
-
not re-derive them. A project still on the six-tier table is read on its own
|
|
468
|
-
scale until `/project-update` renumbers it.
|
|
469
|
-
|
|
470
|
-
In `solo`, discovery, context, impact and risk come from the request plus
|
|
471
|
-
`grep -n`; up to T4 they are a few lines in the conversation, from T5 they are
|
|
472
|
-
recorded stage by stage. Tests run in three scopes: a step runs **only the
|
|
473
|
-
tests it names**, the verification command runs **once, after the last step, to
|
|
474
|
-
the end** (no fail-fast flag; the failing test first for a bugfix), and the
|
|
475
|
-
**e2e suite runs once after that**, at the end of the task — never per step.
|
|
476
|
-
Every failure of the two end-of-task runs is then fixed as **one batch** in a
|
|
477
|
-
`state.py remediate` step before one more run — at most two rounds, then the
|
|
478
|
-
human decides. Review
|
|
479
|
-
findings are handled the same way. Up to T4 the release report is the commit
|
|
480
|
-
message. A subagent is still sent on a stated trigger: an unfamiliar
|
|
481
|
-
area, an `UNKNOWN` the plan depends on, a non-obvious T4+ classification, or
|
|
482
|
-
verification output too long to read inline. Switch to `team` by editing the
|
|
483
|
-
key. No stage is ever skipped; the profile only changes who does it.
|
|
207
|
+
| `ai-guard.py` — **git** | every repository, both runtimes | refuses force push, remote branch delete, history rewrite, push or merge to a protected branch, `gh pr merge`, `--no-verify`, staging a secret, production deploy commands, `npm publish`; a `git commit` or `push` that is not the dialog's pick |
|
|
208
|
+
| `ai-guard.py` — **paths** | where `.ai/` exists | refuses reading or writing `.env`, `secrets/`, keys, dumps, production logs; writing the task state, the journal, `task.md`, the policies' JSON, the compact record, the installed guards; the human-only `state.py` and `update.py` commands from an agent |
|
|
209
|
+
| `ai-guard.py` — **scope** | during an implementation step | refuses editing a file the step does not name, with `SCOPE_CHANGE_REQUIRED` |
|
|
210
|
+
| `ai-guard.py` — **decision** | while a decision is open | holds every tool until the dialog is answered or settled in the terminal |
|
|
211
|
+
| `cap-large-read.py` | Claude Code | refuses an unbounded `Read` over 4 000 lines or 250 KB; a `limit` within the budget passes |
|
|
212
|
+
| `context-guard.py` | both | warns from 80% of the compaction point, snapshots before a compaction, injects the handoff at session start, checks plan drift |
|
|
213
|
+
| `runtime-gate.py` | both | sends the top model's launches one tier down during an outage or at 90% of the weekly limit; asks before an EXPERT launch past the budget |
|
|
214
|
+
| `dialog-gate.py` | both | records your picks from the runtime's dialog; holds the turn while a decision is open |
|
|
215
|
+
|
|
216
|
+
Every path of a Codex `apply_patch` is checked on its own. The guards are
|
|
217
|
+
regex over a command line or a path — a tripwire for the honest mistake, **not
|
|
218
|
+
a security boundary**; the real backstops are branch protection, secrets off
|
|
219
|
+
the developer machine and human review. Every rule, its configuration and
|
|
220
|
+
how a change is proved behaviour-preserving: [docs/hooks.md](docs/hooks.md).
|
|
484
221
|
|
|
485
222
|
## Model tiers
|
|
486
223
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
BALANCED, STRONG and EXPERT is on that plan), the budgets (how many agents may run
|
|
490
|
-
at once and how many of them on STRONG, how far direct mode reaches, whether EXPERT
|
|
491
|
-
runs without asking, and a token figure per tier) and the preferred runtime per
|
|
492
|
-
workflow. `max20.json` is `max.json` with larger budgets; `balanced-max.json` is `max.json`
|
|
493
|
-
with the session pinned to Opus 5.5, no Fable and EXPERT at `high` — only the
|
|
494
|
-
session runs the strong model, every agent keeps its cheap tier. The installer writes the
|
|
495
|
-
resolved object to `<home>/riskrails/profile.json`; `runtime-gate` enforces the
|
|
496
|
-
fan-out and EXPERT budgets by asking, `state.py profile --tier <TIER>` prints a
|
|
497
|
-
tier's model, and `/usage-report --task <id>` compares a task's tokens with its
|
|
498
|
-
plan's figure — reported, never enforced. The prompts themselves name tiers only;
|
|
499
|
-
`tests/test_shared_prompts_model_free.py` keeps it that way.
|
|
224
|
+
The prompts name tiers; the installed profile says which model each tier is on
|
|
225
|
+
your plan and runtime (`state.py profile --tier STRONG`).
|
|
500
226
|
|
|
501
227
|
| Tier | Claude Code | Codex | Does |
|
|
502
228
|
|---|---|---|---|
|
|
503
|
-
| FAST | `haiku`, low | Terra, low | reading and running:
|
|
504
|
-
| BALANCED | `sonnet` | Terra, medium | discovery
|
|
505
|
-
| STRONG | `opus`, high | Sol, high |
|
|
506
|
-
| EXPERT |
|
|
507
|
-
|
|
508
|
-
The main session does the implementation itself. Every agent definition pins its
|
|
509
|
-
own `model:` — most of them the BALANCED tier — and `CLAUDE_CODE_SUBAGENT_MODEL` is
|
|
510
|
-
deliberately not set: before Claude Code v2.1.251 it overrides the frontmatter and
|
|
511
|
-
the per-call model, which put every agent on Sonnet. A STRONG or EXPERT agent runs
|
|
512
|
-
only when a named trigger fires; the triggers are listed in `policies/model-routing.md` of the shared tree.
|
|
513
|
-
|
|
514
|
-
On a Max plan the session runs Opus 5.5 [1m] by default at `medium` effort,
|
|
515
|
-
compacting near 767k tokens (`autoCompactWindow` 800 000, uncapped on a 1M model)
|
|
516
|
-
— `/model opus` gives a 200k session that compacts near 167k, cheaper per turn for
|
|
517
|
-
a small task — and escalates from there: `ai-expert` pins `opus` at `xhigh`, so a session switched to
|
|
518
|
-
Sonnet (the JetBrains agent's Model setting, or `/model`) cannot weaken the last-resort tier. Fable 5.1 [1m] is pinned on `architect` alone, at
|
|
519
|
-
`xhigh`, for design questions outside a task; nothing else ever runs on it, and
|
|
520
|
-
`runtime-gate` sends it to Opus while Fable is rate-limited or its weekly limit is
|
|
521
|
-
nearly used. `--fable no` leaves `architect` on the Opus session as well.
|
|
522
|
-
|
|
523
|
-
Team accounts map by seat: a Standard seat installs as `team-pro` and gets the
|
|
524
|
-
Pro profile, a Premium seat installs as `team-max` and gets the Max profile with
|
|
525
|
-
Fable on `architect`. The installer reads the seat from `~/.claude.json` and
|
|
526
|
-
falls back to `team-pro` when it cannot tell; `--plan team-max` overrides.
|
|
527
|
-
|
|
528
|
-
On Pro and Team Pro the session model is `opusplan`:
|
|
529
|
-
Opus 5 in plan mode, Sonnet 5 when executing. That is the playbook's "plan mode"
|
|
530
|
-
play priced for a $20 plan — the expensive model is spent on the plan, and the
|
|
531
|
-
cheaper one on typing it out. Because a subagent that omits `model:` would
|
|
532
|
-
inherit Sonnet outside plan mode, the installer pins `model: opus` on
|
|
533
|
-
`ai-expert` and `architect` for this plan. `log-reader`, `ai-tester` and
|
|
534
|
-
`ai-release` run at `low` effort: they read and report, they do not think.
|
|
535
|
-
|
|
536
|
-
Under Codex the ChatGPT plan chooses the profile. On Pro the session runs Sol
|
|
537
|
-
at `high` with six agent threads and `ai-expert` runs Astra at `xhigh`. On Plus
|
|
538
|
-
the usage window is a fraction of Pro's, so the session runs Sol at `medium`,
|
|
539
|
-
three agent threads, and `ai-expert` runs Astra at `high` — `xhigh` stays off
|
|
540
|
-
everywhere. The plan is read from `chatgpt_plan_type` in `~/.codex/auth.json`;
|
|
541
|
-
`--codex-plan plus|pro` overrides it, and an install that cannot tell assumes
|
|
542
|
-
Pro and says so.
|
|
543
|
-
|
|
544
|
-
**One difference worth knowing.** Under Claude Code you escalate by spawning
|
|
545
|
-
`ai-risk` or `ai-planner` with `model: opus`. Codex resolves an agent's own file
|
|
546
|
-
*ahead* of the model asked for at spawn time, so that would silently be ignored —
|
|
547
|
-
the Codex roster therefore carries dedicated `ai-risk-strong` and
|
|
548
|
-
`ai-planner-strong` agents that pin Sol. Same tier, same trigger, different
|
|
549
|
-
mechanism. `ai-expert-strong` is the third: the runtime gate reroutes an
|
|
550
|
-
`ai-expert` launch to it while the EXPERT model is unavailable. Every rendered Codex agent writes an explicit `model` and
|
|
551
|
-
`model_reasoning_effort` for the same reason: an omitted `model` falls back to the
|
|
552
|
-
Terra `[agents]` default, which is not the tier a reviewer needs.
|
|
553
|
-
|
|
554
|
-
Why route this way: in measured usage, over 80% of the cost was the main session
|
|
555
|
-
re-reading its context (cache read and write), not output, so the session model is
|
|
556
|
-
the lever that matters. `/usage-report` shows the split on your machine, across
|
|
557
|
-
both runtimes.
|
|
558
|
-
|
|
559
|
-
There is no LOCAL tier: neither runtime has a local-model backend. The work it
|
|
560
|
-
would have done is done by deterministic tools and by `ai-indexer` on the cheapest
|
|
561
|
-
model, and nothing in the design depends on a local model existing.
|
|
562
|
-
|
|
563
|
-
## The guards
|
|
229
|
+
| FAST | `haiku`, low | Terra, low | reading and running: `Explore`, `log-reader` |
|
|
230
|
+
| BALANCED | `sonnet` | Terra, medium | discovery with judgement (`ai-discovery`), the guarded review, batch implementers |
|
|
231
|
+
| STRONG | `opus`, high | Sol, high | the full review, security review, `architect` in `/rr-spec` |
|
|
232
|
+
| EXPERT | Fable 5.1 [1m] on `architect`; Opus without Fable | Astra | what STRONG could not settle |
|
|
564
233
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
| `ai-path-guard` | both | only where `.ai/` exists | refuses reading or writing `.env`, `secrets/`, keys, dumps, production logs; edits to the guards' own config, the task state, the journal, the questions file or the handoff; and `state.py approve` from an agent session — approval is a human's to give |
|
|
569
|
-
| `ai-scope-guard` | both | only during an implementation step | refuses editing a file the approved step does not name, with the `SCOPE_CHANGE_REQUIRED` signal |
|
|
570
|
-
| `cap-large-read.py` | Claude only | every session | refuses an unbounded `Read` of a large file; an explicit `limit` passes |
|
|
571
|
-
| `project-scaffold.sh` | Claude only | `Setup:init` | creates the `docs/sdlc/` and runtime layout on `/init` |
|
|
572
|
-
| `context-guard.py` | both | `UserPromptSubmit` / `PreCompact` / `SessionStart:startup\|resume\|clear\|compact` | reads the context size from the transcript: warns once per 10k from 80% of the compaction point, holds a prompt back once from 120% (the same prompt again passes). Before a compaction it writes a snapshot — edited files, latest instructions verbatim, todo list, git state, `.ai/` task state — and tells the summary what to keep; after it, the snapshot goes back into the context. On every session start it writes `.ai/state/session.json` and, with a task in flight, injects `handoff.md` and the pending questions. The snapshot is Claude-only; everything else crosses to Codex. Fails open |
|
|
573
|
-
| `runtime-gate.py` | both, every plan | `PreToolUse`/`PostToolUse` on `Agent` (Codex: `spawn_agent`, or `collaborationspawn_agent` under multi-agent v2 — matcher `^Agent$\|spawn_agent$`), `SubagentStart`/`SubagentStop`, `StopFailure` (Fable installs), the statusline | sends the top model's launches one tier down while it is rate-limited or unreachable (Fable → Opus; on Codex `ai-expert` → `ai-expert-strong` by rewriting `agent_type`, since a role's pinned model beats the call's); asks before an EXPERT launch or a launch past the plan's fan-out (explains instead on Codex, which cannot ask); records the quota; journals `model_fallback`, and `missed_reroute` when an EXPERT child starts during an outage anyway. After an upgrade, re-trust the changed Codex entries in `/hooks`. `fable-gate.py` and `codex-model-gate.py` are shims that exec it |
|
|
574
|
-
|
|
575
|
-
The three shared guards see Codex's `apply_patch` as well. One `apply_patch` can
|
|
576
|
-
touch many files, so every `*** Add/Update/Delete File:` and `*** Move to:` path
|
|
577
|
-
in the patch is checked separately — a single out-of-scope or protected file
|
|
578
|
-
rejects the whole patch. Split the patch rather than widening the step.
|
|
579
|
-
|
|
580
|
-
Codex has no `StopFailure` event, so on Codex `runtime-gate` attributes a failure at
|
|
581
|
-
`SubagentStop` instead, and only when the evidence points at an EXPERT agent:
|
|
582
|
-
the agent's name in the text, an explicit expert `model`, an agent file pinned to
|
|
583
|
-
the expert model, or an expert launch inside the last five minutes.
|
|
584
|
-
|
|
585
|
-
They are regex-based and run on every matching tool call. That makes them
|
|
586
|
-
defence-in-depth against ordinary agent mistakes — **not a security boundary**. A
|
|
587
|
-
determined process can still read a file through an interpreter, and a tool that
|
|
588
|
-
is not on the hook path is not gated at all. The real backstops are human review
|
|
589
|
-
and server-side branch protection.
|
|
234
|
+
The session implements — Opus 5.5 [1m] on Max, `opusplan` on Pro, Sol on Codex
|
|
235
|
+
— and the expensive tiers run one at a time on a named trigger. Plans,
|
|
236
|
+
budgets, the runtime gate and the Codex differences: [docs/routing.md](docs/routing.md).
|
|
590
237
|
|
|
591
238
|
## Known risks
|
|
592
239
|
|
|
593
|
-
What
|
|
594
|
-
|
|
595
|
-
do. None is a secret. Each was found in a review or a spec, then accepted or
|
|
596
|
-
deferred with a written reason in the file named under **Source**.
|
|
597
|
-
|
|
598
|
-
### The guards are a tripwire, not a sandbox
|
|
599
|
-
|
|
600
|
-
**Risk.** Every guard rule is a regex over a command line or a path. A process
|
|
601
|
-
that wants to get past it can do so. It can read `.env` through a Python
|
|
602
|
-
one-liner, build a command from variables, pipe base64 into a shell, or use a
|
|
603
|
-
tool that is not on the hook path at all.
|
|
604
|
-
**Why it stays.** A real boundary needs an OS-level sandbox. A hook that runs
|
|
605
|
-
before a tool call cannot be one. The guards exist to stop the *honest mistake*,
|
|
606
|
-
like reading `.env` to check a value or editing one file too many.
|
|
607
|
-
**Limits.** A tripwire catches the common obfuscations (quoted flags,
|
|
608
|
-
`--opt=value`, interpreter one-liners that mention `.ai/state/` or
|
|
609
|
-
`.ai/reports/`).
|
|
610
|
-
**What to do.** Keep production secrets off the developer machine, turn on
|
|
611
|
-
server-side branch protection, and review every diff before merge. Those are the
|
|
612
|
-
real backstops.
|
|
613
|
-
**Source.** `docs/hooks.md`, "What these guards are, and are not".
|
|
614
|
-
|
|
615
|
-
### Human approval can be forged by a determined process
|
|
616
|
-
|
|
617
|
-
**Risk.** `state.py approve` refuses to run without a terminal on stdin, and the
|
|
618
|
-
path guard refuses it from an agent session. An obfuscated invocation that gets
|
|
619
|
-
past the regex and also allocates a pseudo-terminal (a pty) can still grant the
|
|
620
|
-
approval.
|
|
621
|
-
**Why it stays.** This is the same regex limit as above. The check is there to
|
|
622
|
-
make self-approval deliberate and visible, not impossible.
|
|
623
|
-
**Limits.** `approve` also refuses when no gate was ever requested
|
|
624
|
-
(`gate_requested` in the journal). Every approval is journalled with who, when
|
|
625
|
-
and how (`via: terminal`).
|
|
626
|
-
**What to do.** Read `state.py events` for the approval before you merge a
|
|
627
|
-
T4–T6 change. An approval you do not remember giving is a finding.
|
|
628
|
-
**Source.** WP2 security review. `hooks/guard/paths.py`, `docs/hooks.md`.
|
|
629
|
-
|
|
630
|
-
### The deletion gate stops an agent's command, not an agent's script
|
|
631
|
-
|
|
632
|
-
**Risk.** `--confirm-delete` of `/project-update` (plain `--apply` and
|
|
633
|
-
`--adopt --cleanup --apply`) is refused two ways: the path guard denies an agent
|
|
634
|
-
Bash command that runs `update.py` with `--apply` and any prefix of
|
|
635
|
-
`--confirm-delete`, and `adopt.py` refuses the flag whenever an agent-session
|
|
636
|
-
marker (`CLAUDECODE`, or Codex's `CODEX_THREAD_ID`, `CODEX_SESSION_ID`,
|
|
637
|
-
`CODEX_CI`, `CODEX_SANDBOX`, `CODEX_SANDBOX_NETWORK_DISABLED`,
|
|
638
|
-
`CODEX_VERSION`) is in its
|
|
639
|
-
environment, even with a terminal on stdin — an agent can open one with
|
|
640
|
-
`script`. The hook matches spellings, so any spelling it does not recognise is
|
|
641
|
-
past it: a script file written with the Write tool, arguments held in a
|
|
642
|
-
variable, command substitution (`$(…)`, backticks), arguments fed through
|
|
643
|
-
`xargs` or stdin, inline `-c` code that imports or runs `update.py`, a glob or a
|
|
644
|
-
renamed copy of it — and a session whose working directory has no `.ai/` above
|
|
645
|
-
it, where the path guard does not run at all. Each of those still needs the
|
|
646
|
-
markers unset (plus a pty) or `AI_UNATTENDED=1` on the command itself to pass
|
|
647
|
-
`adopt.py`, which is deliberate, not an honest mistake. A run with the markers
|
|
648
|
-
stripped under a pty records `via: terminal`, and `adopt.json` /
|
|
649
|
-
`migration.json` are not write-protected, so `via` is a record, not a proof.
|
|
650
|
-
**Why it stays.** Same regex limit as above: the hook sees a command line, not
|
|
651
|
-
what a file it runs will do.
|
|
652
|
-
**Limits.** The record says why the gate passed: `adopt.json` `cleanup.via` and
|
|
653
|
-
`migration.json` `deletions[].via` are `terminal` or `unattended`. A human
|
|
654
|
-
typing through Claude Code's `!` prefix, or in a shell started from an agent
|
|
655
|
-
session, is refused too — that shell carries the markers.
|
|
656
|
-
**What to do.** Run `--confirm-delete` in a terminal of your own. A `via:
|
|
657
|
-
unattended` on a run nobody launched unattended is a finding.
|
|
658
|
-
**Source.** WP6 finding F3, `.ai/reports/T-2026-09-27-003/`.
|
|
659
|
-
`hooks/guard/paths.py`, `skills/project-update/adopt.py` (`human_gate`).
|
|
660
|
-
|
|
661
|
-
### `AI_UNATTENDED=1` turns the human gates off
|
|
662
|
-
|
|
663
|
-
**Risk.** With `AI_UNATTENDED=1` in its environment, any process passes the
|
|
664
|
-
human-present check. That covers the approval gate and the deletion gates of
|
|
665
|
-
`/project-update --confirm-delete` and `--adopt --cleanup`. Those can then
|
|
666
|
-
approve a task or delete files with no human at the keyboard.
|
|
667
|
-
**Why it stays.** CI jobs, cron and scripted batches have no human to take a
|
|
668
|
-
turn. Without the variable they could not run at all.
|
|
669
|
-
**Limits.** It never hides. The approval is recorded permanently as
|
|
670
|
-
`unattended: true` in the state and the journal, `adopt.json` records
|
|
671
|
-
`cleanup.unattended: true` and `cleanup.via: unattended` before the first file
|
|
672
|
-
is removed, `migration.json` records `via: unattended` per deletion, the report prints
|
|
673
|
-
`deleted unattended`, and `/ai-status` names every such event.
|
|
674
|
-
**What to do.** Export it only in the environment of a launcher, never in an
|
|
675
|
-
interactive shell. There it silently disables the gates for every session the
|
|
676
|
-
shell starts. Check `/ai-status` after an unattended run.
|
|
677
|
-
**Source.** `.ai/reports/T-2026-09-21-001/release.md`, "Residual accepted
|
|
678
|
-
risk". `docs/hooks.md`.
|
|
679
|
-
|
|
680
|
-
### The routing hooks are less protected than the guards
|
|
681
|
-
|
|
682
|
-
**Risk.** The path guard protects the three guards' own configuration and the
|
|
683
|
-
shim `codex-model-gate.py` by name. It does **not** yet protect `runtime-gate.py`
|
|
684
|
-
or `context-guard.py`. An agent could edit those two hooks, and so change the
|
|
685
|
-
EXPERT reroute, the quota records or the session and compaction snapshot.
|
|
686
|
-
**Why it stays.** WP5 froze the guard files, so that the characterization golden
|
|
687
|
-
file proves no guard behaviour changed. Protection for both hooks was split off
|
|
688
|
-
as its own additive task.
|
|
689
|
-
**Limits.** The installed copies live under `~/.claude` or `~/.codex`, outside
|
|
690
|
-
any project, and a change to them shows up in `npx riskrails --dry-run`. On Codex an
|
|
691
|
-
edited hook entry loses its trust and stops running until you re-approve it.
|
|
692
|
-
**What to do.** Treat an unexpected diff under `hooks/` like a change to CI
|
|
693
|
-
configuration. Until the follow-up lands, review hook changes by hand.
|
|
694
|
-
**Status.** Open, planned as a follow-up (WP5 OQ3).
|
|
695
|
-
**Source.** `docs/sdlc/specs/adaptive-cross-runtime-sdlc-wp5-runtimes-and-plans.md`, concern 2 and OQ3.
|
|
696
|
-
|
|
697
|
-
### The compaction snapshot runs `git` in the working directory
|
|
698
|
-
|
|
699
|
-
**Risk.** Before a compaction, `context-guard.py` runs `git branch --show-current`
|
|
700
|
-
and `git status --short` in the session's directory. A hostile repository can
|
|
701
|
-
make those calls run its own code through its local git configuration (for
|
|
702
|
-
example `core.fsmonitor`).
|
|
703
|
-
**Why it stays.** This predates the plugin's own work. It is the same exposure as
|
|
704
|
-
any shell prompt that shows the git branch.
|
|
705
|
-
**Limits.** It runs only under Claude Code, only on compaction, and with your
|
|
706
|
-
own user rights. It gives no more than opening the repository in a git-aware
|
|
707
|
-
shell already does.
|
|
708
|
-
**What to do.** Do not run agent sessions inside repositories you do not trust.
|
|
709
|
-
**Status.** Open.
|
|
710
|
-
**Source.** WP2 security review. `hooks/context-guard.py` (`build_snapshot`).
|
|
711
|
-
|
|
712
|
-
### Codex runs only hooks you re-approved
|
|
713
|
-
|
|
714
|
-
**Risk.** Codex keeps each hook approval as a hash of the whole `config.toml`
|
|
715
|
-
entry. After an upgrade changes an entry, for example the gate's matcher
|
|
716
|
-
`^Agent$|spawn_agent$`, Codex **skips that hook** until you approve it again in
|
|
717
|
-
`/hooks`. Until then the scope, path and git guards, or the runtime gate, are
|
|
718
|
-
simply not running under Codex, and nothing on screen says so.
|
|
719
|
-
**Why it stays.** This is how Codex's trust model works. A plugin cannot and
|
|
720
|
-
should not approve itself.
|
|
721
|
-
**What to do.** After every `npx riskrails` that touches Codex, open `/hooks` in
|
|
722
|
-
Codex and approve the changed entries. The installer ends with that reminder.
|
|
723
|
-
**Source.** `docs/hooks.md`, runtime gate. `README.md`, "What it installs".
|
|
724
|
-
|
|
725
|
-
### Codex facts are observed, not guaranteed
|
|
726
|
-
|
|
727
|
-
**Risk.** Codex's multi-agent v2 launches agents as `collaborationspawn_agent`.
|
|
728
|
-
A role's pinned model beats a model given at launch, and a Codex hook cannot ask
|
|
729
|
-
the user a question. All three were established by live probes and one
|
|
730
|
-
documentation page, and Codex changes quickly. A future Codex release can make
|
|
731
|
-
the reroute or the fan-out check silently miss.
|
|
732
|
-
**Limits.** On Codex these rules degrade to "allow and explain" by design, never
|
|
733
|
-
to a block. The `SubagentStart` backstop journals `missed_reroute` when an
|
|
734
|
-
EXPERT agent starts during an outage anyway.
|
|
735
|
-
**What to do.** After a Codex upgrade, run `state.py events --type
|
|
736
|
-
missed_reroute`. Any line there means the reroute stopped matching.
|
|
737
|
-
**Source.** WP5 spec, accepted concern 6. PR #26.
|
|
738
|
-
|
|
739
|
-
### Budgets and quota are advice, not limits
|
|
740
|
-
|
|
741
|
-
**Risk.** Several numbers can be off, and none of them ever blocks work.
|
|
742
|
-
- **Quota signals can be stale.** Codex quota comes only from the rollouts of a
|
|
743
|
-
past or running session. Claude's comes only while a session with the
|
|
744
|
-
statusline runs.
|
|
745
|
-
- **The running-agent count can drift.** A `SubagentStop` lost to a crash leaves
|
|
746
|
-
an entry behind for up to 30 minutes, which costs one extra question.
|
|
747
|
-
- **The EXPERT launch count per task never shrinks.** Declined launches count
|
|
748
|
-
too.
|
|
749
|
-
- **On Pro/Plus, "strictly serial" is the plugin's rule, not Codex's.** Codex
|
|
750
|
-
still allows 3 threads and can run a second agent after the gate has explained
|
|
751
|
-
why it should not.
|
|
752
|
-
|
|
753
|
-
**What to do.** `runtime-gate.py clear` empties the count after a crash. Read
|
|
754
|
-
the quota with its `stale` and `resets_at` fields, not as a hard figure.
|
|
755
|
-
**Source.** WP5 spec, accepted concerns 1, 3 and 4. `hooks/runtime-gate.py`.
|
|
756
|
-
|
|
757
|
-
### `--adopt` edge cases fail safe, not smoothly
|
|
758
|
-
|
|
759
|
-
Three low-severity cases from the WP6 review were accepted. Each fails closed or
|
|
760
|
-
safe rather than losing data.
|
|
761
|
-
- An interrupted apply's `planned_writes` excuses those paths from the
|
|
762
|
-
clean-tree check. It never decides what is written or deleted.
|
|
763
|
-
- On resume, `original/<file>` is reused, so edits made between the crash and
|
|
764
|
-
the resume are not in the saved original. Git still has the committed version.
|
|
765
|
-
- In a monorepo subdirectory an interrupted apply cannot resume until you commit.
|
|
766
|
-
It refuses rather than guesses.
|
|
767
|
-
|
|
768
|
-
Two things have not been tried by hand yet: `--adopt` on a real Spec Kit or
|
|
769
|
-
Cursor project (the tests use fixtures), and a cleanup run in a real terminal.
|
|
770
|
-
**What to do.** Run `--adopt` on a clean, committed tree, read the dry run
|
|
771
|
-
first, and keep `original/` until you have checked the result.
|
|
772
|
-
**Source.** `.ai/reports/T-2026-09-21-001/review.md`, the LOW rows.
|
|
773
|
-
|
|
774
|
-
### The usage report's parse cache trusts mtime and size
|
|
775
|
-
|
|
776
|
-
**Risk.** `usage-report.py` keeps each transcript's parse state on disk, keyed by
|
|
777
|
-
path, mtime and size, and folds only the bytes past the offset it stopped at. A
|
|
778
|
-
file that was rewritten rather than appended to — same path, larger, and the
|
|
779
|
-
same first 256 bytes — would be resumed from a stale offset, and the report
|
|
780
|
-
would count the old records together with the new ones.
|
|
781
|
-
**Why it stays.** The alternative is hashing every transcript on every run,
|
|
782
|
-
which is the work the cache exists to avoid. Transcripts are append-only in both
|
|
783
|
-
runtimes; a rewrite that also preserves the opening bytes is not a case either
|
|
784
|
-
runtime produces.
|
|
785
|
-
**Limits.** A shrunk file, one whose first block changed, or one whose stored
|
|
786
|
-
state is not the shape and contents this version folds, is parsed from zero.
|
|
787
|
-
Same mtime and size means the file is not read again — unless its last line had
|
|
788
|
-
no newline after it, which is re-read until it is complete — so an in-place
|
|
789
|
-
rewrite of exactly the same length is the other side of the same assumption.
|
|
790
|
-
The cache is a report's cache: nothing else reads it, and a wrong number is a
|
|
791
|
-
wrong number in a report, not a wrong change to a repository.
|
|
792
|
-
**What to do.** `--no-cache` reports without it, and tells you within one run
|
|
793
|
-
whether a surprising number came from the cache. Deleting
|
|
794
|
-
`~/.cache/riskrails/usage-report.json` rebuilds it from scratch.
|
|
795
|
-
**Source.** This entry; `skills/usage-report/SKILL.md`, "The parse cache".
|
|
796
|
-
|
|
797
|
-
## Migrating from claude-routing
|
|
798
|
-
|
|
799
|
-
This plugin absorbed `claude-routing`. Run `npx riskrails` once and it:
|
|
800
|
-
|
|
801
|
-
- writes the routing settings itself, from the same `profiles/` — nothing about
|
|
802
|
-
your model, effort or limits changes;
|
|
803
|
-
- **removes the `<!-- claude-routing:start/end -->` block** from
|
|
804
|
-
`~/.claude/CLAUDE.md` and writes one `riskrails` block carrying both sets
|
|
805
|
-
of rules;
|
|
806
|
-
- also removes an older unmarked `# Model allocation by task and scope` section,
|
|
807
|
-
if one predates the plugins;
|
|
808
|
-
- **retires `agents/reviewer.md`** to `reviewer.md.superseded`, because
|
|
809
|
-
`ai-reviewer` replaces it — adversarial, tier-aware, and reading the project's
|
|
810
|
-
own policies. If you edited that file, it is left alone and the conflict is
|
|
811
|
-
reported instead;
|
|
812
|
-
- keeps every other hook, agent and settings key you have, including ones no
|
|
813
|
-
plugin owns.
|
|
814
|
-
|
|
815
|
-
Where the two disagreed, the agentic layer wins:
|
|
816
|
-
|
|
817
|
-
| Question | Answer |
|
|
818
|
-
|---|---|
|
|
819
|
-
| Who reviews a change before commit? | `ai-reviewer`, not `reviewer` |
|
|
820
|
-
| Where does a change start? | `/ai-task`. The `/sdlc-*` chain is for work needing a written intent and spec first, then hands its plan to `/ai-task` |
|
|
821
|
-
| Who designs? | `ai-planner` inside a task, `ai-expert` on escalation. `architect` is for design questions outside a task, or in a repository without `.ai/` |
|
|
822
|
-
| Which init? | `/ai-init`, which runs the SDLC scaffold too. `/project-init` is the SDLC layout alone |
|
|
240
|
+
What 3.0 knowingly does not protect against, each with why it stays and what
|
|
241
|
+
to do: [docs/known-risks.md](docs/known-risks.md).
|
|
823
242
|
|
|
824
|
-
|
|
825
|
-
|
|
243
|
+
| Risk | In one line |
|
|
244
|
+
|---|---|
|
|
245
|
+
| The guards are a tripwire, not a sandbox | a determined process gets past a regex |
|
|
246
|
+
| Human approval can be forged by a determined process | a pty, or a written ledger, can approve; every approval is journalled |
|
|
247
|
+
| The deletion gate stops a command, not a script | run `--confirm-delete` in a terminal of your own |
|
|
248
|
+
| `AI_UNATTENDED=1` turns the human gates off | export it only in a launcher, never in a shell |
|
|
249
|
+
| Three hooks are less protected than the guards | review hook diffs by hand |
|
|
250
|
+
| The compaction snapshot runs `git` in the working directory | do not run agents in repositories you do not trust |
|
|
251
|
+
| Codex runs only hooks you re-approved | open `/hooks` after every upgrade |
|
|
252
|
+
| Codex facts are observed, not guaranteed | grep the journals for `missed_reroute` after a Codex upgrade |
|
|
253
|
+
| The dialog switch is read in two places | when the dialog and the terminal disagree, use the terminal |
|
|
254
|
+
| Budgets and quota are advice, not limits | read the quota with its `stale` field |
|
|
255
|
+
| `--adopt` edge cases fail safe, not smoothly | adopt on a committed tree, keep `original/` |
|
|
256
|
+
| The usage report's parse cache trusts mtime and size | `--no-cache` when a number surprises you |
|
|
826
257
|
|
|
827
258
|
## Tests
|
|
828
259
|
|
|
829
260
|
```bash
|
|
830
|
-
|
|
261
|
+
python3 -I tests/run_suite.py --serial test_guard_characterization.TestTimed --serial test_dialog_gate.TestGateFastPath
|
|
262
|
+
python3 -I -m unittest discover -s tests -p 'test_*.py' # CI's serial oracle, same test ids
|
|
263
|
+
AI_E2E=1 python3 -I -m unittest discover -s tests -p test_end_to_end.py
|
|
831
264
|
```
|
|
832
265
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
settings
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
runtimes, including its incremental parse cache against the same run with the
|
|
846
|
-
cache off; and both escalation gates through every event.
|
|
847
|
-
|
|
848
|
-
No suite reads or writes the developer's real `~/.claude` or `~/.codex`.
|
|
266
|
+
Fifty-six suites, 979 tests, about 75 seconds in parallel. The guards against JSON fixtures for both runtimes and a
|
|
267
|
+
golden of 1 341 recorded decisions, byte for byte, with timed cases that hold
|
|
268
|
+
the hot path under its budget; the state machine, the weight, the batch loop,
|
|
269
|
+
the dialog contract and the documents rule; the compact record and its
|
|
270
|
+
pre-push validator; the installer for every plan and `--target`, the Codex
|
|
271
|
+
agent renderer and the `config.toml` merge against a fixture full of
|
|
272
|
+
third-party settings; `/rr-setup --update` against projects scaffolded from
|
|
273
|
+
the oldest shipped templates and edited by hand; `--adopt` against Spec Kit,
|
|
274
|
+
Kiro and Cursor fixtures; and an end-to-end run that installs both runtimes
|
|
275
|
+
into scratch directories, drives one task through the guards from Claude and
|
|
276
|
+
then from Codex, and proves both see the same state. No suite reads or writes
|
|
277
|
+
the developer's real `~/.claude` or `~/.codex`.
|
|
849
278
|
|
|
850
279
|
## Documentation
|
|
851
280
|
|
|
852
281
|
| File | About |
|
|
853
282
|
|---|---|
|
|
854
|
-
|
|
|
855
|
-
|
|
|
856
|
-
|
|
|
857
|
-
|
|
|
858
|
-
|
|
|
859
|
-
|
|
|
860
|
-
|
|
|
861
|
-
|
|
|
862
|
-
|
|
|
283
|
+
| [docs/getting-started.md](docs/getting-started.md) | the first hour: install, `/rr-setup`, a light and a full task, a batch build, the update, the bill |
|
|
284
|
+
| [docs/installation.md](docs/installation.md) | every installer option, what it installs per runtime, what loads on every turn, the limits it sets, the 2.x home-side upgrade |
|
|
285
|
+
| [docs/project-update.md](docs/project-update.md) | shared versus project-owned files, how a file is updated, the migrations and their rollback, `--adopt` |
|
|
286
|
+
| [docs/architecture.md](docs/architecture.md) | how the pieces fit: the installed home, `.ai/`, the pipeline, the state, the hooks, the two adapters |
|
|
287
|
+
| [docs/weight.md](docs/weight.md) | the six axes, the three modes, what is measured and what is declared, tuning the table |
|
|
288
|
+
| [docs/workflows.md](docs/workflows.md) | feature, bugfix, refactoring, hotfix, investigation |
|
|
289
|
+
| [docs/agents.md](docs/agents.md) | the five agents, their contracts, the escalation ladder |
|
|
290
|
+
| [docs/routing.md](docs/routing.md) | the tiers per plan and runtime, the runtime gate, the Codex differences |
|
|
291
|
+
| [docs/hooks.md](docs/hooks.md) | every rule the guards enforce, their configuration and limits |
|
|
292
|
+
| [docs/hook-performance.md](docs/hook-performance.md) | the hot path's budget and how a change is proved behaviour-preserving |
|
|
293
|
+
| [docs/local-memory.md](docs/local-memory.md) | the compact record: task ids, packages, recall, the pre-push check, migration |
|
|
294
|
+
| [docs/known-risks.md](docs/known-risks.md) | what the guards do not protect against, and what to do about it |
|
|
295
|
+
| [docs/faq.md](docs/faq.md) | why something was blocked, and how to change it |
|
|
296
|
+
| [docs/sdlc/README.md](docs/sdlc/README.md) | the spec → plan → build artefacts in a project |
|
|
297
|
+
| [ROADMAP.md](ROADMAP.md) | what is decided but not built; the open rows |
|
|
863
298
|
|
|
864
299
|
## Contributing, security and license
|
|
865
300
|
|
|
866
|
-
- [CONTRIBUTING.md](CONTRIBUTING.md): setup, tests,
|
|
867
|
-
- [SECURITY.md](SECURITY.md): what counts as a vulnerability and how to report
|
|
868
|
-
one privately
|
|
301
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): setup, tests, how a guard change is proved, how a change gets merged
|
|
302
|
+
- [SECURITY.md](SECURITY.md): what counts as a vulnerability and how to report one privately
|
|
869
303
|
- [LICENSE](LICENSE): MIT
|