riskrails 3.0.1 → 3.2.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 +1 -1
- package/README.md +29 -15
- package/ai/policies/model-routing.md +7 -7
- package/ai/policies/production.md +3 -3
- package/ai/policies/release.md +2 -2
- package/ai/policies/review-economy.md +27 -30
- package/bin/riskrails.js +114 -23
- package/hooks/ai-path-guard-defaults.json +3 -2
- package/hooks/context-guard.py +15 -12
- package/hooks/dialog-gate.py +1 -1
- package/hooks/guard/common.py +2 -0
- package/hooks/guard/dialog.py +107 -27
- package/hooks/guard/git.py +12 -3
- package/hooks/guard/paths.py +150 -37
- package/hooks/project-scaffold.py +3 -6
- package/hooks/runtime-gate.py +134 -33
- package/hooks/update_notice.py +22 -16
- package/instructions/routing.md +1 -1
- package/instructions/stub.md +3 -3
- package/package.json +1 -1
- package/skills/AGENTS.md +1 -1
- package/skills/CLAUDE.md +1 -1
- package/skills/ai-init/templates/.ai/AGENTS.md +1 -1
- package/skills/ai-init/templates/.ai/VERSION +1 -1
- package/skills/ai-init/templates/.ai/policies/security.md +7 -1
- package/skills/ai-init/templates/.ai/policies/testing.md +19 -6
- package/skills/ai-init/templates/.ai/state/README.md +4 -4
- package/skills/ai-init/templates/AGENTS.block.md +2 -2
- package/skills/ai-init/templates/CLAUDE.block.md +2 -2
- package/skills/ai-init/templates/GEMINI.block.md +2 -2
- package/skills/ai-init/templates/gitignore.snippet +1 -0
- package/skills/ai-init/templates/junie-guidelines.block.md +2 -2
- package/skills/ai-task/rr_state/__init__.py +1 -0
- package/skills/ai-task/rr_state/cli.py +210 -0
- package/skills/ai-task/rr_state/close.py +171 -0
- package/skills/ai-task/rr_state/dialog.py +541 -0
- package/skills/ai-task/rr_state/dialog_cmd.py +164 -0
- package/skills/ai-task/rr_state/fs.py +220 -0
- package/skills/ai-task/rr_state/gate.py +125 -0
- package/skills/ai-task/rr_state/gate_cmd.py +128 -0
- package/skills/ai-task/rr_state/handoff.py +226 -0
- package/skills/ai-task/rr_state/journal.py +126 -0
- package/skills/ai-task/rr_state/labels.py +99 -0
- package/skills/ai-task/rr_state/ledger.py +145 -0
- package/skills/ai-task/rr_state/locks.py +84 -0
- package/skills/ai-task/rr_state/memory_cmd.py +310 -0
- package/skills/ai-task/rr_state/policy.py +378 -0
- package/skills/ai-task/rr_state/profile.py +373 -0
- package/skills/ai-task/rr_state/questions.py +336 -0
- package/skills/ai-task/rr_state/questions_cmd.py +321 -0
- package/skills/ai-task/rr_state/report.py +271 -0
- package/skills/ai-task/rr_state/report_cmd.py +230 -0
- package/skills/ai-task/rr_state/review.py +165 -0
- package/skills/ai-task/rr_state/runtime.py +80 -0
- package/skills/ai-task/rr_state/start.py +257 -0
- package/skills/ai-task/rr_state/steps.py +131 -0
- package/skills/ai-task/rr_state/task.py +396 -0
- package/skills/ai-task/rr_state/verify.py +231 -0
- package/skills/ai-task/sensors.py +37 -4
- package/skills/ai-task/state.py +24 -5138
- package/skills/project-init/templates/constitution.md +1 -1
- package/skills/project-init/templates/sdlc-README.md +20 -14
- package/skills/project-update/adopt.py +38 -24
- package/skills/project-update/history/blobs/00289f98434fed230eebe768dfb3425f83d737866a3aebc97aa95167c4fa0c6e +131 -0
- package/skills/project-update/history/blobs/079a91f976ad69b5cf49ccb9a28849576e4735ddd9485fc26599e528a69fd2f4 +30 -0
- package/skills/project-update/history/blobs/07ff7fa6f93d471ff533f553b4e523e4105ccde1f3dab70132c5237728f68422 +13 -0
- package/skills/project-update/history/blobs/0ef4a360c76da290173dea0f8073949cb0f8113710a38df95acae5d96bdd1286 +123 -0
- package/skills/project-update/history/blobs/18c7a2753ce9cb6f6d3d1e9d5d1125d75950b879d56b7325225b200869631b29 +15 -0
- package/skills/project-update/history/blobs/25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e +1 -0
- package/skills/project-update/history/blobs/48d9950f16e31d55475f566a80dc95f64b35f1c804d2f51729e313a4f29bac8d +13 -0
- package/skills/project-update/history/blobs/5a93f78b4ed4d416c171139b2ce610a117f9c1dd46fbb56ca86729cb4c53d10c +6 -0
- package/skills/project-update/history/blobs/6a3be0e624e5ad91e44d08857b7183ee53b7903e1e8d56ff196dbc9dde271e4a +75 -0
- package/skills/project-update/history/blobs/7170553410bba904ffb7edb4b231e3b3579b85542d84de717299980938f82c9d +13 -0
- package/skills/project-update/history/blobs/774b1e3a04ef0d49d33c44a2cfecd92eb6f6b738a12b6a681103ba7fc13e7b86 +67 -0
- package/skills/project-update/history/blobs/7f063e79195a5c85c37b7260dd20c184d3b9bea85e37202e0bbca73ad84bba49 +13 -0
- package/skills/project-update/history/blobs/a51517e13e57420d2d88a6bcfb4055d762d4a0d10a8f88450929c5c4572213a9 +67 -0
- package/skills/project-update/history/blobs/c8573ca1f810ce6c4ac49456a4c1725b93e0d3b74c54ce95d46d8747777895f0 +13 -0
- package/skills/project-update/history/blobs/d6e5b34d475e7187d2a8cb2aac7e7f1a427142baf6461bda387d70ceb4d8a8c1 +30 -0
- package/skills/project-update/history/blobs/e120ff4e32b2e950fc94ada4e787d57dc9c790da37223e5a202d74f7e3aadf80 +13 -0
- package/skills/project-update/history/index.json +38 -16
- package/skills/project-update/migrations/0003_context_diet.py +1 -1
- package/skills/project-update/migrations/0007_weight_model.py +130 -23
- package/skills/project-update/migrations/0011_retire_risk_tiers.py +37 -0
- package/skills/project-update/migrations/README.md +16 -1
- package/skills/project-update/migrations/__init__.py +6 -1
- package/skills/project-update/render_instructions.py +1 -1
- package/skills/project-update/update.py +569 -106
- package/skills/rr-build-all/SKILL.md +2 -2
- package/skills/rr-plan/SKILL.md +3 -1
- package/skills/rr-review/SKILL.md +13 -8
- package/skills/rr-setup/SKILL.md +24 -4
- package/skills/rr-setup/setup.py +7 -1
- package/skills/rr-spec/SKILL.md +4 -2
- package/skills/rr-status/status.py +36 -9
- package/skills/usage-report/usage-report.py +4 -2
- package/skills/ai-init/templates/.ai/policies/risk-tiers.json +0 -628
- package/skills/ai-init/templates/.ai/policies/risk-tiers.md +0 -180
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "A weight-based engineering pipeline for Claude Code and Codex: each change is weighed on six axes, runs light, guarded or full, edits only the files its plan names, and ends at human approval — enforced by hooks, reviewed adversarially, routed to the cheap model tier by default.",
|
|
8
|
-
"version": "3.
|
|
8
|
+
"version": "3.2.1"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"source": "url",
|
|
15
15
|
"url": "https://github.com/vanssata/claude-agentic-sdlc-module.git"
|
|
16
16
|
},
|
|
17
|
-
"version": "3.
|
|
17
|
+
"version": "3.2.1",
|
|
18
18
|
"strict": true
|
|
19
19
|
}
|
|
20
20
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "riskrails",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "A weight-based engineering pipeline for Claude Code and Codex: each change is weighed on six axes, runs light, guarded or full, edits only the files its plan names, and ends at human approval — enforced by hooks, reviewed adversarially, routed to the cheap model tier by default.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ivan Kakurov"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "riskrails",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "A weight-based engineering pipeline for Claude Code and Codex: each change is weighed on six axes, runs light, guarded or full, edits only the files its plan names, and ends at human approval — enforced by hooks, reviewed adversarially, routed to the cheap model tier by default.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ivan Kakurov"
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# RiskRails
|
|
2
2
|
|
|
3
|
-
[](https://github.com/vanssata/claude-agentic-sdlc-module/releases/tag/v3.2.1)
|
|
4
4
|
[](docs/routing.md)
|
|
5
5
|
[](docs/known-risks.md)
|
|
6
6
|
[](LICENSE)
|
|
@@ -18,7 +18,7 @@ approval. Risky actions are stopped by a hook, not by a polite request in a
|
|
|
18
18
|
prompt.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx riskrails@3.
|
|
21
|
+
npx riskrails@3.2.1 # detects Claude Code, Codex or both (Node 18+, Python 3.11+)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Then, in any project: `/rr-setup` once, and `/rr-build <what you want>` for
|
|
@@ -50,9 +50,21 @@ Tuned for **one developer who knows the codebase**, on a Claude Pro, Max or
|
|
|
50
50
|
Team plan, or a ChatGPT Plus or Pro plan, following the
|
|
51
51
|
[AI-native SDLC playbook](https://claude.com/blog/the-ai-native-sdlc-playbook).
|
|
52
52
|
|
|
53
|
-
**Version 3.
|
|
53
|
+
**Version 3.2.1.** The guards have limits: read [Known risks](docs/known-risks.md)
|
|
54
54
|
before relying on them. What is decided but not built is in [ROADMAP.md](ROADMAP.md).
|
|
55
55
|
|
|
56
|
+
## Upgrade to 3.1
|
|
57
|
+
|
|
58
|
+
From 3.0.x: update the home (`npx riskrails@3.2.1`, restart the runtime), then
|
|
59
|
+
in each project `/rr-setup --update` (dry run first, then `--apply`). Schema 10
|
|
60
|
+
becomes **11**: migration `0011` proposes deleting `.ai/policies/risk-tiers.json`
|
|
61
|
+
and `risk-tiers.md`, which `weight.json` and `weight.md` replaced; confirm with
|
|
62
|
+
`--apply --confirm-delete <name>` in a terminal of your own. The update now
|
|
63
|
+
renders `weight.md` from `weight.json` instead of merging it, reads the
|
|
64
|
+
`docs/sdlc/*/TEMPLATE.md` files from the plugin (an unedited project copy is
|
|
65
|
+
offered for deletion), and proposes cleanup of old run logs and stale tasks.
|
|
66
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
67
|
+
|
|
56
68
|
## Upgrade to 3.0
|
|
57
69
|
|
|
58
70
|
3.0 renames the plugin from `claude-agentic` to `riskrails`, replaces the risk
|
|
@@ -70,7 +82,7 @@ version. Step by step, in this order:
|
|
|
70
82
|
3. **Update the home** on every machine, for every runtime:
|
|
71
83
|
|
|
72
84
|
```bash
|
|
73
|
-
npx riskrails@3.
|
|
85
|
+
npx riskrails@3.2.1 # or: npx -y riskrails@latest --yes
|
|
74
86
|
```
|
|
75
87
|
|
|
76
88
|
It moves `<home>/claude-agentic/` to `<home>/riskrails/` and leaves a
|
|
@@ -85,22 +97,22 @@ version. Step by step, in this order:
|
|
|
85
97
|
/rr-setup --update # dry run first; read it; then --apply
|
|
86
98
|
```
|
|
87
99
|
|
|
88
|
-
|
|
100
|
+
Migrations `0007` to `0010` (and `0011`, see Upgrade to 3.1) run inside the dry run, in order, before the merge:
|
|
89
101
|
`0007` creates `.ai/policies/weight.json`, carrying over the project's
|
|
90
102
|
edits to its risk-tier table; `0008` renames the managed-block markers and
|
|
91
103
|
the router's routes; `0009` moves `current.json` to `state.json`; `0010`
|
|
92
104
|
creates `.ai/policies/dialog.json` with the dialog on. Edited markdown is
|
|
93
105
|
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.
|
|
106
|
+
`delete?` proposals are left for you to confirm. The schema becomes 10 with a 3.0 plugin, 11 with 3.1.
|
|
95
107
|
5. **Decide two things the migrations cannot.** Keep the dialog (then amend
|
|
96
108
|
constitution C5 as `0010` hints) or set `"enabled": false`; and tune the
|
|
97
109
|
weight table — `domain.modules`, `customer_visible`, `hot_paths`,
|
|
98
110
|
`needs_step`, `irreversible` — then `state.py weight --mirror`.
|
|
99
|
-
6. **Verify**: `/rr-status` shows schema
|
|
111
|
+
6. **Verify**: `/rr-status` shows schema 11 and no task in flight; a
|
|
100
112
|
`/rr-build` on a small change runs light and closes on the two facts.
|
|
101
113
|
Commit `.ai/`.
|
|
102
114
|
|
|
103
|
-
Every machine of a team upgrades the plugin before a project at schema
|
|
115
|
+
Every machine of a team upgrades the plugin before a project at schema 11 is
|
|
104
116
|
committed; an older plugin refuses such a project. Rollback and the full
|
|
105
117
|
migration table are in [docs/project-update.md](docs/project-update.md); the
|
|
106
118
|
home-side details in [docs/installation.md](docs/installation.md).
|
|
@@ -108,12 +120,13 @@ home-side details in [docs/installation.md](docs/installation.md).
|
|
|
108
120
|
## Install
|
|
109
121
|
|
|
110
122
|
```bash
|
|
111
|
-
npx riskrails@3.
|
|
112
|
-
npx riskrails@3.
|
|
113
|
-
npx riskrails@3.
|
|
114
|
-
npx riskrails@3.
|
|
115
|
-
npx riskrails@3.
|
|
116
|
-
npx riskrails@3.
|
|
123
|
+
npx riskrails@3.2.1 # detects Claude Code, Codex, or both; proposes the detected plan
|
|
124
|
+
npx riskrails@3.2.1 --target codex # auto | claude | codex | both
|
|
125
|
+
npx riskrails@3.2.1 --plan max --fable no # pro | team-pro | team-max | max | max20 | balanced-max
|
|
126
|
+
npx riskrails@3.2.1 --codex-plan plus # plus | pro | balanced-max
|
|
127
|
+
npx riskrails@3.2.1 --context economy # the plan's compaction window; the context guard also blocks
|
|
128
|
+
npx riskrails@3.2.1 --statusline own # keep your own statusline command (from 3.2.1; default: the plugin's two-line statusline)
|
|
129
|
+
npx riskrails@3.2.1 --dry-run # print what would be written, write nothing
|
|
117
130
|
npx -y riskrails@latest --yes # update in place, keeping the recorded plan
|
|
118
131
|
```
|
|
119
132
|
|
|
@@ -263,7 +276,7 @@ python3 -I -m unittest discover -s tests -p 'test_*.py' # CI's serial oracl
|
|
|
263
276
|
AI_E2E=1 python3 -I -m unittest discover -s tests -p test_end_to_end.py
|
|
264
277
|
```
|
|
265
278
|
|
|
266
|
-
Fifty-
|
|
279
|
+
Fifty-nine suites, 1045 tests, about 75 seconds in parallel. The guards against JSON fixtures for both runtimes and a
|
|
267
280
|
golden of 1 341 recorded decisions, byte for byte, with timed cases that hold
|
|
268
281
|
the hot path under its budget; the state machine, the weight, the batch loop,
|
|
269
282
|
the dialog contract and the documents rule; the compact record and its
|
|
@@ -293,6 +306,7 @@ the developer's real `~/.claude` or `~/.codex`.
|
|
|
293
306
|
| [docs/local-memory.md](docs/local-memory.md) | the compact record: task ids, packages, recall, the pre-push check, migration |
|
|
294
307
|
| [docs/known-risks.md](docs/known-risks.md) | what the guards do not protect against, and what to do about it |
|
|
295
308
|
| [docs/faq.md](docs/faq.md) | why something was blocked, and how to change it |
|
|
309
|
+
| [CHANGELOG.md](CHANGELOG.md) | what changed in each release, and the upgrade notes |
|
|
296
310
|
| [docs/sdlc/README.md](docs/sdlc/README.md) | the spec → plan → build artefacts in a project |
|
|
297
311
|
| [ROADMAP.md](ROADMAP.md) | what is decided but not built; the open rows |
|
|
298
312
|
|
|
@@ -19,16 +19,16 @@ not settle — never for fact collection.
|
|
|
19
19
|
|
|
20
20
|
Which model a tier is depends on the plan and the runtime: the installed profile
|
|
21
21
|
decides it, `state.py profile --tier <TIER>` prints it for the runtime you are in,
|
|
22
|
-
and `/
|
|
22
|
+
and `/rr-status` shows the plan's budget. This file names tiers only, so it is true on
|
|
23
23
|
every plan.
|
|
24
24
|
|
|
25
25
|
The main session runs the model the installed profile sets and does the
|
|
26
26
|
implementation itself. Planning, the weight, the test run and the release are
|
|
27
27
|
its work too, not an agent's: `/rr-plan`, `state.py weight`, `state.py verify`,
|
|
28
28
|
`/rr-ship`. Which stages spawn an agent at all
|
|
29
|
-
is decided by
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
is decided by the mode `state.py weight` gives: light runs direct — no review
|
|
30
|
+
unless `close` reports a red fact — guarded has one BALANCED review, full one
|
|
31
|
+
STRONG review, and nothing runs on STRONG below full — and tests run once
|
|
32
32
|
after the last step, to the end, with every failure fixed as one batch.
|
|
33
33
|
|
|
34
34
|
**Reading by difficulty.** A lookup — where X is, one route, service id, config
|
|
@@ -65,12 +65,12 @@ depends on a local model; if one becomes available it slots in at the FAST tier.
|
|
|
65
65
|
|
|
66
66
|
## EXPERT triggers
|
|
67
67
|
|
|
68
|
-
1. The task is
|
|
68
|
+
1. The task is full and irreversible: migration, infrastructure, production architecture.
|
|
69
69
|
2. STRONG could not settle it: `confidence: uncertain`, or two STRONG results
|
|
70
70
|
contradict each other.
|
|
71
71
|
3. An irreversible design with several viable options — core data model, public
|
|
72
72
|
API or event contract, service split.
|
|
73
|
-
4. The final check of a
|
|
73
|
+
4. The final check of a full-and-irreversible plan or of a production-incident root cause before a
|
|
74
74
|
human acts on it.
|
|
75
75
|
|
|
76
76
|
## Escalation
|
|
@@ -104,7 +104,7 @@ the model `state.py profile --tier STRONG` printed — because a tier alone does
|
|
|
104
104
|
cost or how much weight to give it. Report the model that **ran**, not the one
|
|
105
105
|
that was requested: a gate may have sent an EXPERT agent down one tier during a
|
|
106
106
|
rate limit, and a Codex agent file may have overridden the spawn-time model. Both
|
|
107
|
-
runtimes expose the current state through `/
|
|
107
|
+
runtimes expose the current state through `/rr-status`.
|
|
108
108
|
|
|
109
109
|
## What never gets delegated
|
|
110
110
|
|
|
@@ -7,8 +7,8 @@ is reached by a real customer until you have evidence it is not.
|
|
|
7
7
|
|
|
8
8
|
1. Find the callers. `grep`/`rg` for the method, the service id, the route, the
|
|
9
9
|
event name, the template.
|
|
10
|
-
2. Find the tests that pin the current behaviour. If none exist and the
|
|
11
|
-
|
|
10
|
+
2. Find the tests that pin the current behaviour. If none exist and the mode is
|
|
11
|
+
guarded or full, write a characterization test **first** — one that passes against
|
|
12
12
|
the code as it is today.
|
|
13
13
|
3. Check the history for why it looks like this: `git log -L`, `git log -S`,
|
|
14
14
|
`git blame`. A workaround with a ticket number in the message is a constraint,
|
|
@@ -37,6 +37,6 @@ named in the release report. "It worked locally" is not observability.
|
|
|
37
37
|
|
|
38
38
|
## Rollback
|
|
39
39
|
|
|
40
|
-
Every
|
|
40
|
+
Every guarded or full change states how to undo it. If undoing it is impossible — a data
|
|
41
41
|
migration, an external system call, a fiscal record — say so plainly in the
|
|
42
42
|
release report, because that changes who has to approve it.
|
package/ai/policies/release.md
CHANGED
|
@@ -29,7 +29,7 @@ It exists so that the human approving the change can see, in one page, what
|
|
|
29
29
|
changed, what deliberately did not, what was tested, what was reviewed, what
|
|
30
30
|
could go wrong, and how to undo it.
|
|
31
31
|
|
|
32
|
-
A report that says "no known risks" for a
|
|
32
|
+
A report that says "no known risks" for a guarded or full change is a report that was not
|
|
33
33
|
written carefully.
|
|
34
34
|
|
|
35
35
|
## What blocks a release
|
|
@@ -38,7 +38,7 @@ written carefully.
|
|
|
38
38
|
`focus: security` pass;
|
|
39
39
|
- a new regression in the last `state.py verify` run;
|
|
40
40
|
- a destructive database operation without its own named approval;
|
|
41
|
-
- a
|
|
41
|
+
- a guarded or full change with no rollback section;
|
|
42
42
|
- an unanswered blocking question in the plan.
|
|
43
43
|
|
|
44
44
|
## Project specifics
|
|
@@ -72,8 +72,8 @@ The ledger is what replaces agent memory. Without it, every pass is pass one.
|
|
|
72
72
|
## 3. Findings are fixed as one batch, and re-review is scoped, not repeated
|
|
73
73
|
|
|
74
74
|
A review returns all its findings at once, and they are fixed at once: one
|
|
75
|
-
`state.py
|
|
76
|
-
findings name, every BLOCKER and HIGH addressed in it, then the verification
|
|
75
|
+
fix batch (`state.py start --amend` widens the task to the finished steps plus
|
|
76
|
+
what the findings name), every BLOCKER and HIGH addressed in it, then the verification
|
|
77
77
|
command once. Never one fix, one run, one re-review per finding — that is the
|
|
78
78
|
serial loop the measurements below describe, and the remediation of one finding
|
|
79
79
|
is where the next regression comes from.
|
|
@@ -98,13 +98,12 @@ shipped), each after a fix batch — before the same stop: the pass past that
|
|
|
98
98
|
number is the human's. The reviewer's verdict says which closing rule applies:
|
|
99
99
|
`blockers_open` (a BLOCKER) takes the remediation and the scoped re-review;
|
|
100
100
|
`pass_with_findings` (a HIGH, no BLOCKER) takes the remediation and a green
|
|
101
|
-
verification run, no re-review; `pass` closes. `state.py
|
|
102
|
-
|
|
103
|
-
after the green run, and the close summary names the labels that run closed.
|
|
101
|
+
verification run, no re-review; `pass` closes. `state.py review --verdict`
|
|
102
|
+
takes all three values.
|
|
104
103
|
|
|
105
|
-
## 3a. The boundary of a
|
|
104
|
+
## 3a. The boundary of a light or guarded review
|
|
106
105
|
|
|
107
|
-
|
|
106
|
+
In light and guarded the review is bounded. Its inputs are the task goal, the diff, the
|
|
108
107
|
tests the step names, and the project's own fixture corpus and threat model —
|
|
109
108
|
`.ai/policies/safety.md`, the guards' `*-defaults.json`, the test fixtures.
|
|
110
109
|
The reviewer works its whole list inside that corpus; what it may not do is
|
|
@@ -117,8 +116,8 @@ it only towards `yes`.
|
|
|
117
116
|
|
|
118
117
|
A guard whose input is any shell line is a bottomless well for an unbounded
|
|
119
118
|
reviewer: every pass finds a new input nobody will type, and every fix for one
|
|
120
|
-
is the next pass's target. The boundary is what lets a review end.
|
|
121
|
-
|
|
119
|
+
is the next pass's target. The boundary is what lets a review end. In full
|
|
120
|
+
mode there is none — the reviewer's list applies unbounded, as the cost of being
|
|
122
121
|
wrong there warrants.
|
|
123
122
|
|
|
124
123
|
## 4. Split one pass into parallel dimensions, not serial rounds
|
|
@@ -174,37 +173,35 @@ script, or run it yourself:
|
|
|
174
173
|
A review pass spent on a self-contradictory risk register is a pass not spent on
|
|
175
174
|
the code. When a task closes a gap that a register entry calls open, the entry is
|
|
176
175
|
corrected **in the same step as the fix**, not later. When the plan gains a
|
|
177
|
-
remediation, the plan gains a section —
|
|
176
|
+
remediation, the plan gains a section — in guarded and full the artefact a human
|
|
178
177
|
approves must describe the change that exists, and a diff wider than the plan is
|
|
179
178
|
invisible to the path guard, because the file set never changed.
|
|
180
179
|
|
|
181
180
|
## 8. The one thing the sensors may replace
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
review is replaced by the sensor set and
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
other.
|
|
182
|
+
In light mode, when the two facts `close` measures are green **on the tree as
|
|
183
|
+
it is now** — a green whole-suite `verify` and every changed file inside the
|
|
184
|
+
task's files — the model review is replaced by the sensor set and the task
|
|
185
|
+
closes without one. This is the intent's decision 6, and it is an exception to
|
|
186
|
+
the rule below, written here so the two do not contradict each other.
|
|
189
187
|
|
|
190
188
|
It is narrow on purpose. The skip requires that the suite passed on this exact
|
|
191
|
-
tree, that the tests the plan named **fail without the change** (`bite` — a
|
|
192
|
-
green suite that never reaches the diff is not evidence)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
work.
|
|
189
|
+
tree, and that the tests the plan named **fail without the change** (`bite` — a
|
|
190
|
+
green suite that never reaches the diff is not evidence). The mode is the one
|
|
191
|
+
re-scored from the real diff: a diff that raises it to guarded or full keeps the
|
|
192
|
+
review. A missing linter is not green: `unavailable` keeps the review, and only
|
|
193
|
+
a human writing `lint_command: none` says the project has no linter. No
|
|
194
|
+
sensor skips the review by asserting it — `close` reaches it from
|
|
195
|
+
measurements.
|
|
196
|
+
|
|
197
|
+
In guarded and full the review always runs. What the sensors change there is
|
|
198
|
+
where it starts: their rows are in the ledger, CONFIRMED, before the reviewer
|
|
199
|
+
plans its work.
|
|
203
200
|
|
|
204
201
|
## What this policy will not trade away
|
|
205
202
|
|
|
206
|
-
- No review stage the
|
|
207
|
-
the
|
|
203
|
+
- No review stage the mode requires is skipped, shortened, or merged — except
|
|
204
|
+
the light-mode model review, and only under §8's measured conditions.
|
|
208
205
|
- No reviewer is moved to a cheaper model to go faster. Narrow the surface, never
|
|
209
206
|
the mind.
|
|
210
207
|
- A blocker still returns to implementation, however late it arrives. Past the
|
package/bin/riskrails.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// npx riskrails [--target auto|claude|codex|both] [--plan pro|team-pro|team-max|max|max20|balanced-max]
|
|
4
4
|
// [--fable auto|yes|no] [--codex-plan plus|pro|balanced-max] [--dry-run]
|
|
5
5
|
// [--context default|economy] [--statusline-context yes|no]
|
|
6
|
-
// [--home DIR] [--codex-home DIR] [--yes] [--version]
|
|
6
|
+
// [--statusline riskrails|own] [--home DIR] [--codex-home DIR] [--yes] [--version]
|
|
7
7
|
// --target defaults to auto: each runtime is installed only if its executable is on PATH.
|
|
8
8
|
// --plan Claude only: pro, team-pro (Team Standard seat), team-max (Team Premium seat), max
|
|
9
9
|
// or max20 (Max 20x: max's settings, larger budgets). Defaults to auto-detect from
|
|
@@ -22,9 +22,17 @@
|
|
|
22
22
|
// Max, 300 000 on Pro) and the guard also holds a prompt back past 120% of it.
|
|
23
23
|
// Without economy, an autoCompactWindow equal to the plan's is removed; any other
|
|
24
24
|
// value is the user's and stays. Recorded in riskrails/profile.json as `context`.
|
|
25
|
-
// --statusline-context yes (default) shows the model and
|
|
26
|
-
// statusline:
|
|
27
|
-
// and `context-used` in Codex's [tui] status_line. no
|
|
25
|
+
// --statusline-context yes (default) shows the model, effort and context use in the
|
|
26
|
+
// statusline: the first line in Claude Code (`--context`), `model-with-reasoning`
|
|
27
|
+
// and `context-used` in Codex's [tui] status_line. no leaves that line out (Claude:
|
|
28
|
+
// removes `--context`; Codex: adds nothing and removes nothing); not recorded.
|
|
29
|
+
// --statusline Claude only. riskrails (default) writes the plugin's statusline on every
|
|
30
|
+
// install and update: the model, effort and context line and the prompt line
|
|
31
|
+
// (user@host:cwd | branch | 5h/7d limits | cost), over a statusline command of your
|
|
32
|
+
// own (the old command is printed and kept in riskrails/statusline.json). own keeps
|
|
33
|
+
// your command behind the quota check, as before 3.2, and brings a replaced one
|
|
34
|
+
// back. The choice is recorded in riskrails/statusline.json; a later install
|
|
35
|
+
// without --statusline keeps it.
|
|
28
36
|
// --home / --codex-home the runtime homes (default ~/.claude and ~/.codex; the environment
|
|
29
37
|
// variables CLAUDE_DIR and CODEX_DIR mean the same). Naming exactly one of them under
|
|
30
38
|
// --target auto installs that runtime alone.
|
|
@@ -56,7 +64,7 @@ let CODEX_DIR = env.CODEX_DIR || path.join(os.homedir(), '.codex');
|
|
|
56
64
|
const HOME = env.HOME || os.homedir();
|
|
57
65
|
let PLAN = '', FABLE = 'auto', DRY = false, TARGET = 'auto', PLAN_GIVEN = false;
|
|
58
66
|
let CODEX_PLAN = '', CODEX_PLAN_GIVEN = false, YES = false;
|
|
59
|
-
let CONTEXT = 'default', STATUSLINE_CONTEXT = 'yes';
|
|
67
|
+
let CONTEXT = 'default', STATUSLINE_CONTEXT = 'yes', STATUSLINE = 'riskrails', STATUSLINE_GIVEN = false;
|
|
60
68
|
|
|
61
69
|
function version() {
|
|
62
70
|
try { return JSON.parse(fs.readFileSync(path.join(SRC, 'package.json'), 'utf8')).version || 'unknown'; }
|
|
@@ -88,6 +96,7 @@ function usage() {
|
|
|
88
96
|
case '--codex-plan': CODEX_PLAN = take(); CODEX_PLAN_GIVEN = true; break;
|
|
89
97
|
case '--context': CONTEXT = take(); break;
|
|
90
98
|
case '--statusline-context': STATUSLINE_CONTEXT = take(); break;
|
|
99
|
+
case '--statusline': STATUSLINE = take(); STATUSLINE_GIVEN = true; break;
|
|
91
100
|
case '--home': CLAUDE_DIR = path.resolve(take()); CLAUDE_DIR_GIVEN = true; break;
|
|
92
101
|
case '--codex-home': CODEX_DIR = path.resolve(take()); CODEX_DIR_GIVEN = true; break;
|
|
93
102
|
case '--dry-run': DRY = true; break;
|
|
@@ -105,6 +114,7 @@ if (!['default', 'economy'].includes(CONTEXT)) die(`--context must be default|ec
|
|
|
105
114
|
if (!['yes', 'no'].includes(STATUSLINE_CONTEXT)) {
|
|
106
115
|
die(`--statusline-context must be yes|no (got '${STATUSLINE_CONTEXT}')`, 2);
|
|
107
116
|
}
|
|
117
|
+
if (!['riskrails', 'own'].includes(STATUSLINE)) die(`--statusline must be riskrails|own (got '${STATUSLINE}')`, 2);
|
|
108
118
|
const major = Number(process.versions.node.split('.')[0]);
|
|
109
119
|
if (major < 18) die('riskrails needs Node 18 or newer (this is ' + process.versions.node + ')', 1);
|
|
110
120
|
|
|
@@ -117,6 +127,25 @@ function onPath(cmd) {
|
|
|
117
127
|
return false;
|
|
118
128
|
}
|
|
119
129
|
if (!onPath('python3')) die('python3 is required', 1);
|
|
130
|
+
{
|
|
131
|
+
const pv = spawnSync('python3', ['-c', "import sys; print('%d.%d' % sys.version_info[:2])"],
|
|
132
|
+
{ env, encoding: 'utf8', timeout: 10000, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
133
|
+
const lines = (pv.stdout || '').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
134
|
+
const found = (lines.length ? lines[lines.length - 1] : '').match(/^(\d+)\.(\d+)$/) || [];
|
|
135
|
+
if (!found.length || Number(found[1]) < 3 || (Number(found[1]) === 3 && Number(found[2]) < 11)) {
|
|
136
|
+
let why = '';
|
|
137
|
+
if (!found.length) {
|
|
138
|
+
const errLine = ((pv.stderr || '').split('\n').map((l) => l.trim()).find(Boolean)) || '';
|
|
139
|
+
const parts = [];
|
|
140
|
+
if (pv.error) parts.push(`error ${pv.error.code || pv.error.message}`);
|
|
141
|
+
if (pv.status !== null && pv.status !== undefined && pv.status !== 0) parts.push(`exit status ${pv.status}`);
|
|
142
|
+
if (pv.signal) parts.push(`signal ${pv.signal}`);
|
|
143
|
+
if (errLine) parts.push(`stderr: ${errLine}`);
|
|
144
|
+
why = parts.length ? ` - ${parts.join(', ')}` : ' - no version on stdout';
|
|
145
|
+
}
|
|
146
|
+
die(`python3 3.11 or newer is required (found ${found.length ? found[0] : 'unknown'}${why})`, 1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
120
149
|
|
|
121
150
|
// ------------------------------------------------------------------ helpers
|
|
122
151
|
const exists = (p) => fs.existsSync(p); // follows symlinks, as [ -e ]
|
|
@@ -553,11 +582,34 @@ function shSplit(s) { // shlex.split,
|
|
|
553
582
|
return words;
|
|
554
583
|
}
|
|
555
584
|
|
|
556
|
-
// The wrapper is `<PREFIX> [--context] [--then '<the user's command>']`; --context
|
|
557
|
-
// (--statusline-context yes) adds a line with the model and
|
|
585
|
+
// The wrapper is `<PREFIX> [--context] [--line] [--then '<the user's command>']`; --context
|
|
586
|
+
// (--statusline-context yes) adds a line with the model, effort and context use, --line
|
|
587
|
+
// (--statusline riskrails, the default) the plugin's prompt line. With riskrails the
|
|
588
|
+
// command is exactly `<PREFIX> [--context] --line`: a command of the user's is replaced
|
|
589
|
+
// (printed, and kept in <home>/riskrails/statusline.json: the dated settings.json backup is
|
|
590
|
+
// replaced by a second run the same day); with own it stays behind --then, and own brings
|
|
591
|
+
// a replaced command back. The mode is recorded there too: a run without --statusline
|
|
592
|
+
// keeps the recorded one, so the update notice's `--yes` does not undo own.
|
|
558
593
|
function statuslineGate(file, gate, mode) {
|
|
559
594
|
const PREFIX = '"$HOME/.claude/hooks/runtime-gate.py" statusline';
|
|
560
|
-
const
|
|
595
|
+
const RECORD = path.join(path.dirname(file), 'riskrails', 'statusline.json');
|
|
596
|
+
let record = {};
|
|
597
|
+
try { const r = JSON.parse(read(RECORD)); if (isObj(r)) record = r; } catch (e) { /* none yet */ }
|
|
598
|
+
const MODE = STATUSLINE_GIVEN ? STATUSLINE : (['riskrails', 'own'].includes(record.mode) ? record.mode : 'riskrails');
|
|
599
|
+
const OWN = MODE === 'own';
|
|
600
|
+
const kept = typeof record.replaced === 'string' && record.replaced.trim() ? record.replaced : '';
|
|
601
|
+
let saved = ''; // a command of the user's this run replaces
|
|
602
|
+
const persist = () => {
|
|
603
|
+
if (mode === 'dry') return;
|
|
604
|
+
const next = Object.assign({}, record, { mode: MODE },
|
|
605
|
+
saved ? { replaced: saved, replaced_at: BACKUP_DATE } : {});
|
|
606
|
+
if (JSON.stringify(next) === JSON.stringify(record)) return;
|
|
607
|
+
mkdirp(path.dirname(RECORD));
|
|
608
|
+
fs.writeFileSync(RECORD + '.tmp', asciiJSON(next) + '\n');
|
|
609
|
+
fs.renameSync(RECORD + '.tmp', RECORD);
|
|
610
|
+
};
|
|
611
|
+
const CTX = STATUSLINE_CONTEXT === 'yes' ? `${PREFIX} --context` : PREFIX;
|
|
612
|
+
const WANT = OWN ? CTX : `${CTX} --line`;
|
|
561
613
|
const OLD_PREFIX = '"$HOME/.claude/hooks/fable-gate.py" statusline';
|
|
562
614
|
let settings = {};
|
|
563
615
|
if (exists(file)) {
|
|
@@ -570,26 +622,64 @@ function statuslineGate(file, gate, mode) {
|
|
|
570
622
|
line.command = PREFIX + cmd.slice(OLD_PREFIX.length);
|
|
571
623
|
ours = true; gate = 'moved';
|
|
572
624
|
}
|
|
573
|
-
// What follows the prefix and
|
|
574
|
-
const tail = () => line.command.slice(PREFIX.length).trim().replace(
|
|
625
|
+
// What follows the prefix and its --context/--line flags: the user's --then, byte for byte.
|
|
626
|
+
const tail = () => line.command.slice(PREFIX.length).trim().replace(/^((--context|--line)(\s+|$))+/, '');
|
|
627
|
+
// The user's own command inside a `--then '<command>'` tail, for the replaced message.
|
|
628
|
+
const wrapped = (rest) => {
|
|
629
|
+
// an unparsable tail is not recorded: an earlier good `replaced` stays, the dated backup holds the line
|
|
630
|
+
try { const parts = shSplit(rest); return parts.length > 1 ? parts[1] : ''; } catch (e) { return ''; }
|
|
631
|
+
};
|
|
575
632
|
const shown = STATUSLINE_CONTEXT === 'yes' ? ' with the model and the context use' : '';
|
|
633
|
+
const plugin = STATUSLINE_CONTEXT === 'yes'
|
|
634
|
+
? 'the model, effort and context line and the prompt line' : 'the prompt line';
|
|
635
|
+
const backup = '--statusline own brings it back';
|
|
576
636
|
let action = null;
|
|
577
637
|
if (gate === 'moved') {
|
|
578
638
|
const rest = tail();
|
|
579
|
-
|
|
580
|
-
|
|
639
|
+
if (OWN) {
|
|
640
|
+
line.command = rest ? `${WANT} ${rest}` : WANT;
|
|
641
|
+
action = 'moved the fable-gate statusline wrapper to runtime-gate (your command is unchanged)';
|
|
642
|
+
} else {
|
|
643
|
+
line.command = WANT;
|
|
644
|
+
saved = rest.startsWith('--then') ? wrapped(rest) : '';
|
|
645
|
+
action = `moved the fable-gate statusline wrapper to runtime-gate, with the plugin's statusline (${plugin})` +
|
|
646
|
+
(saved ? `; replaced your statusline command: ${saved} (${backup})` : '');
|
|
647
|
+
}
|
|
581
648
|
} else if (gate === 'on') {
|
|
582
649
|
if (ours) {
|
|
583
650
|
const rest = tail();
|
|
584
|
-
const
|
|
585
|
-
|
|
651
|
+
const hadLine = /(^|\s)--line(\s|$)/.test(cmd.slice(PREFIX.length));
|
|
652
|
+
// one-shot: only the run that switches to own restores; once own is recorded, a bare line stays bare
|
|
653
|
+
const restore = OWN && !rest && kept && record.mode !== 'own';
|
|
654
|
+
saved = !OWN && rest.startsWith('--then') ? wrapped(rest) : '';
|
|
655
|
+
const want = restore ? `${WANT} --then ${shQuote(kept)}` : OWN && rest ? `${WANT} ${rest}` : WANT;
|
|
656
|
+
if (want === cmd) {
|
|
657
|
+
persist();
|
|
658
|
+
return [OWN ? 'statusline: already records the quota' : "statusline: already the plugin's statusline"];
|
|
659
|
+
}
|
|
586
660
|
line.command = want;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
661
|
+
if (saved) {
|
|
662
|
+
action = `replaced your statusline command with the plugin's statusline (${plugin}): ${saved} (${backup})`;
|
|
663
|
+
} else if (restore) {
|
|
664
|
+
action = `restored your statusline command behind the quota check${shown}: ${kept}`;
|
|
665
|
+
} else if (!OWN) {
|
|
666
|
+
action = `set the plugin's statusline (${plugin})`;
|
|
667
|
+
} else if (hadLine) {
|
|
668
|
+
action = rest ? `removed the plugin's prompt line; your command runs behind the quota check${shown}`
|
|
669
|
+
: `removed the plugin's prompt line; no command of yours to restore, so the quota check runs${shown} alone`;
|
|
670
|
+
} else {
|
|
671
|
+
action = STATUSLINE_CONTEXT === 'yes'
|
|
672
|
+
? 'added the model and the context use to the statusline wrapper (your command is unchanged)'
|
|
673
|
+
: 'removed the model and context line from the statusline wrapper (your command is unchanged)';
|
|
674
|
+
}
|
|
590
675
|
} else if (line === undefined || line === null) {
|
|
591
676
|
settings.statusLine = { type: 'command', command: WANT };
|
|
592
|
-
action = `added a statusline that records the quota (and the Fable weekly limit)${shown}
|
|
677
|
+
action = OWN ? `added a statusline that records the quota (and the Fable weekly limit)${shown}`
|
|
678
|
+
: `added the plugin's statusline (${plugin}), which also records the quota`;
|
|
679
|
+
} else if (!OWN && isObj(line) && line.type === 'command' && typeof cmd === 'string' && cmd.trim()) {
|
|
680
|
+
line.command = WANT;
|
|
681
|
+
saved = cmd;
|
|
682
|
+
action = `replaced your statusline command with the plugin's statusline (${plugin}): ${cmd} (${backup})`;
|
|
593
683
|
} else if (isObj(line) && line.type === 'command' && typeof cmd === 'string' && cmd.trim()) {
|
|
594
684
|
line.command = `${WANT} --then ${shQuote(cmd)}`;
|
|
595
685
|
action = `wrapped your statusline command with the quota check${shown} (its output is unchanged)`;
|
|
@@ -609,6 +699,7 @@ function statuslineGate(file, gate, mode) {
|
|
|
609
699
|
}
|
|
610
700
|
}
|
|
611
701
|
if (mode === 'dry') return ['statusline: would have ' + action];
|
|
702
|
+
persist(); // the replaced command first: never lost
|
|
612
703
|
fs.writeFileSync(file + '.tmp', asciiJSON(settings) + '\n');
|
|
613
704
|
fs.renameSync(file + '.tmp', file);
|
|
614
705
|
return ['statusline: ' + action];
|
|
@@ -1205,9 +1296,9 @@ function claudeSummary() {
|
|
|
1205
1296
|
'Restart Claude Code, then:',
|
|
1206
1297
|
' /config model and effort match the profile',
|
|
1207
1298
|
` /hooks lists cap-large-read, ai-guard, project-scaffold and context-guard, plus runtime-gate on PreToolUse, PostToolUse, SubagentStart, SubagentStop${C.GATE === 'on' ? ' and StopFailure' : ''}`,
|
|
1208
|
-
' /skills lists
|
|
1209
|
-
' /
|
|
1210
|
-
' /
|
|
1299
|
+
' /skills lists rr-setup, rr-spec, rr-plan, rr-build, rr-build-all, rr-review, rr-ship, rr-status',
|
|
1300
|
+
' /rr-setup in a project, to survey it and build .ai/',
|
|
1301
|
+
' /rr-setup --update in a project that already has .ai/ or docs/sdlc/, to pull in these rules',
|
|
1211
1302
|
];
|
|
1212
1303
|
out(lines.join('\n'));
|
|
1213
1304
|
}
|
|
@@ -1411,8 +1502,8 @@ function codexSummary() {
|
|
|
1411
1502
|
'',
|
|
1412
1503
|
'Restart Codex, then:',
|
|
1413
1504
|
' /hooks review and trust the guard, gate and context hooks — until you do, Codex skips them',
|
|
1414
|
-
' /skills lists
|
|
1415
|
-
' /
|
|
1505
|
+
' /skills lists rr-setup, rr-spec, rr-plan, rr-build, rr-build-all, rr-review, rr-ship, rr-status',
|
|
1506
|
+
' /rr-setup in a project, to survey it and build .ai/',
|
|
1416
1507
|
'',
|
|
1417
1508
|
`Your previous default model was replaced. backups/${BACKUP_DATE}/config.toml holds the old one.`,
|
|
1418
1509
|
].join('\n'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Shipped defaults for ai-path-guard. Do not edit this file; a project overrides it in .ai/policies/path-guard.json (patterns are unioned, allow wins over every other list). Patterns are POSIX extended regular expressions matched against the absolute path. deny_patterns are secrets: refused for read and write. protected_config_patterns are always on and refuse writes: the task's state and journal, task.md, the compact record (.ai/memory/, .ai/local/memory/), and the guards' own configuration, engine and state.py under .claude/ or .codex/, in a project or in the user's home, and the riskrails plugin's cache folder, which carries the same code on the plugin route. A project's dependency_instruction_patterns, from before 3.4, are read as deny_patterns. task_protected_patterns are on only while a task is in flight (.ai/state/state.json, or else current.json, exists and has not reached \"done\"): the runtime's settings, Claude's record of installed plugins and the pipeline's policies, so a run cannot change the rules it is being judged by. Reading anything but a secret is allowed.",
|
|
2
|
+
"_comment": "Shipped defaults for ai-path-guard. Do not edit this file; a project overrides it in .ai/policies/path-guard.json (patterns are unioned, allow wins over every other list). Patterns are POSIX extended regular expressions matched against the absolute path. deny_patterns are secrets: refused for read and write. protected_config_patterns are always on and refuse writes: the task's state and journal, task.md, the compact record (.ai/memory/, .ai/local/memory/), and the guards' own configuration, engine and state.py under .claude/ or .codex/, in a project or in the user's home, and the riskrails plugin's cache folder, which carries the same code on the plugin route. Also the installer's statusline record (.claude/riskrails/statusline.json), which keeps the statusline command it replaced. A project's dependency_instruction_patterns, from before 3.4, are read as deny_patterns. task_protected_patterns are on only while a task is in flight (.ai/state/state.json, or else current.json, exists and has not reached \"done\"): the runtime's settings, Claude's record of installed plugins and the pipeline's policies, so a run cannot change the rules it is being judged by. Reading anything but a secret is allowed.",
|
|
3
3
|
"deny_patterns": [
|
|
4
4
|
"(^|/)\\.env$",
|
|
5
5
|
"(^|/)\\.env\\.(local|prod|production|staging|live)$",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"(^|/)\\.(claude|codex)/hooks/runtime-gate\\.py$",
|
|
39
39
|
"(^|/)\\.(claude|codex)/hooks/(guard|lib)/",
|
|
40
40
|
"(^|/)\\.(claude|codex)/skills/ai-task/",
|
|
41
|
-
"(^|/)\\.(claude|codex)/plugins/cache/[^/]+/riskrails(/|$)"
|
|
41
|
+
"(^|/)\\.(claude|codex)/plugins/cache/[^/]+/riskrails(/|$)",
|
|
42
|
+
"(^|/)\\.claude/(riskrails|claude-agentic)/statusline\\.json$"
|
|
42
43
|
],
|
|
43
44
|
"task_protected_patterns": [
|
|
44
45
|
"(^|/)\\.claude/settings[^/]*\\.json$",
|