riskrails 3.2.1 → 4.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 +1 -1
- package/README.md +40 -83
- package/agents/ai-reviewer.md.tmpl +1 -1
- package/ai/using-riskrails.md +0 -3
- package/bin/riskrails.js +13 -59
- package/hooks/ai-path-guard-defaults.json +1 -1
- package/hooks/context-guard.py +2 -4
- package/hooks/guard/paths.py +3 -6
- package/hooks/guard/scope.py +1 -3
- package/hooks/runtime-gate.py +18 -58
- package/instructions/routing.md +2 -2
- package/package.json +1 -1
- package/scripts/ai-path +1 -1
- package/scripts/resolve-profile.py +0 -3
- package/skills/ai-audit/templates/ai-sdlc-adoption-plan.md +1 -1
- package/skills/ai-audit/templates/play-scoring-rubric.md +1 -1
- package/skills/ai-init/templates/.ai/AGENTS.md +6 -6
- package/skills/ai-init/templates/.ai/VERSION +1 -1
- package/skills/ai-init/templates/.ai/policies/security.md +1 -2
- package/skills/ai-init/templates/.ai/policies/testing.md +5 -12
- package/skills/ai-init/templates/.ai/policies/verification.md +18 -0
- package/skills/ai-init/templates/.ai/policies/weight.json +0 -1
- package/skills/ai-init/templates/.ai/policies/weight.md +1 -2
- package/skills/ai-init/templates/.ai/policies/weight.project.json +4 -0
- package/skills/ai-init/templates/.ai/project/architecture.md +1 -1
- package/skills/ai-init/templates/.ai/project/initial-assessment.md +2 -2
- package/skills/ai-init/templates/.ai/project/overview.md +1 -1
- package/skills/ai-init/templates/.ai/rules/README.md +2 -2
- package/skills/ai-init/templates/.ai/state/README.md +1 -1
- package/skills/ai-task/memory_store.py +8 -9
- package/skills/ai-task/rr_state/cli.py +1 -1
- package/skills/ai-task/rr_state/dialog.py +2 -4
- package/skills/ai-task/rr_state/fs.py +5 -13
- package/skills/ai-task/rr_state/profile.py +2 -4
- package/skills/ai-task/rr_state/questions_cmd.py +1 -1
- package/skills/ai-task/rr_state/report.py +3 -4
- package/skills/ai-task/rr_state/runtime.py +1 -1
- package/skills/ai-task/rr_state/start.py +7 -7
- package/skills/ai-task/sensors.py +10 -11
- package/skills/ai-task/state.py +4 -24
- package/skills/ai-task/weight.py +17 -17
- package/skills/ai-task/weight_overlay.py +38 -0
- package/skills/project-init/templates/constitution.md +2 -2
- package/skills/project-update/history/blobs/00f5e27ce2d66e3a1d8b222b013ae8fa1fed32612d91a551e0c661cee3ef9c2d +34 -0
- package/skills/project-update/history/blobs/0a6b1b6dd06a35582b2e9989a1f41c40abc29254b8e482714fcc6f0a7b16c406 +32 -0
- package/skills/project-update/history/blobs/0aeb04b04262683cac6513545683486a73dc473b447bba43b5aaab18403957a6 +34 -0
- package/skills/project-update/history/blobs/0ce913a28996f546f72d570b0e5a376362e993ce4d805bc4aae57d38caa44212 +41 -0
- package/skills/project-update/history/blobs/0ea9e36446b624b73b2a821231b2ee66a357b43d584a5b23da48b3c800855e65 +41 -0
- package/skills/project-update/history/blobs/0fb1d68678a1349195b2e79433bb20174d25874e3bfd0b70410bb4e428b4e4b8 +35 -0
- package/skills/project-update/history/blobs/119b2b9d0f9c647a243540724edac6b29afa020c69aa205797bcfa9eb06bc021 +35 -0
- package/skills/project-update/history/blobs/1d0fe3ee5fb8260ba610942d4bbdfff7410fbe80ed826ac07f32985031dc2b3f +34 -0
- package/skills/project-update/history/blobs/1de9a37ed6402893dcab6e61ddbf15ffd35f15a6d84b18e675a0c5bf4e981f85 +42 -0
- package/skills/project-update/history/blobs/1f44e1a236233dcbab58064f39b69f798b35d1d9a29f8c615b5b12854ce633cb +15 -0
- package/skills/project-update/history/blobs/1f824645299579cbdd8141642ddbb4bca9fd5fe9d3112f036d8221f0811e0abf +40 -0
- package/skills/project-update/history/blobs/235fb88393cfe20dd58b35fe9785cc45baf5ffe21a13d926652d76ec68cea8b7 +35 -0
- package/skills/project-update/history/blobs/3918b4ee011d9477b9d2779e2893ab599749c406d4b09439a9748b64fbff58f3 +18 -0
- package/skills/project-update/history/blobs/449273200dfec2a56885c881ff34d40e8ecc00da53c24c2d09b7fbeb1004d255 +40 -0
- package/skills/project-update/history/blobs/4c45aee9b9fc5f6b4e1d9739e434c4094975a5a8dec6b9703fb25f3080680c77 +124 -0
- package/skills/project-update/history/blobs/4dd3fa12bc45acc8c836ab65d790248ca7504a021bca6677499d17bb3d798ed9 +64 -0
- package/skills/project-update/history/blobs/51e3f15095d7123ea8566b512479ee10a6cb2f336b7352a259a083a887bd7116 +33 -0
- package/skills/project-update/history/blobs/5497a204967bea7dab29079a6e1dfa3d8713b2d2cb294c2779378039f03a55fc +36 -0
- package/skills/project-update/history/blobs/60c4d7e7fde7049d92d84295fc13ca9a91eb2de423be08f093b3ee5d5e817ff8 +64 -0
- package/skills/project-update/history/blobs/6221b7c14cc04634e485e562d464c6860d45666a7f63e293c06bb2944ff556cc +27 -0
- package/skills/project-update/history/blobs/637971cfb844ef4285e2886a4b1c834fb2ed5c46e773adb88c2c147fea626ad3 +33 -0
- package/skills/project-update/history/blobs/65af2282eea2a51fcc27a4acf2c26c727b0961c30e1be13176369200c460c219 +34 -0
- package/skills/project-update/history/blobs/66ecd73e303e7bbc1aa8ee72435d0d10812ff7b2eff0adac59bbf0a303e2cc01 +41 -0
- package/skills/project-update/history/blobs/6a43bf32461b22b87fafe2865529dc04fa66cd00182a2a7598aa071667ca255a +35 -0
- package/skills/project-update/history/blobs/6d6dee8634abf9ac057bb206828b83a893a2afcf4f86bbe69e839a3b47d0adde +48 -0
- package/skills/project-update/history/blobs/7224ede803ea81e65a456e21b6028a0e848e8a880456c6503eebe7534ca4f6aa +74 -0
- package/skills/project-update/history/blobs/765a39a202617096c232017822ae738d37e4405430c71700dc7bc14bf1ba794e +33 -0
- package/skills/project-update/history/blobs/77577440a933d1cabd8c6f4a6401cf361a784aa09a25500a6f7b44ead8ab5e55 +41 -0
- package/skills/project-update/history/blobs/7bae90789625d6fb9071ecb412d14a8d1eb0bc5659d8d284c4eaf213e1b5de56 +67 -0
- package/skills/project-update/history/blobs/7bcdf9a0e6b85277b9b60669150224b9414d2e32821ff06f4d148eb1d18785bb +42 -0
- package/skills/project-update/history/blobs/7f834103e9d97aea44e76717bcdcbbe349c560e5b81a42ce85a1cec341041281 +4 -0
- package/skills/project-update/history/blobs/7fb3acb0211818efebc2234863db0b8c846022a7280a6d2d69f267dbf63db48c +34 -0
- package/skills/project-update/history/blobs/87f0855859ff68cc6c988cc0b631ee7f56af7fbba062172bf80e01f4982cc054 +35 -0
- package/skills/project-update/history/blobs/8abab74759766d02b5cb7b0c3e59a3d440b25a454216cd8fea325c23fd822d36 +33 -0
- package/skills/project-update/history/blobs/a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164 +1 -0
- package/skills/project-update/history/blobs/a3df7268feb0780be0d2cf9acced6ed81780a7790f36fe024b6ea0b1bd6eb7a6 +35 -0
- package/skills/project-update/history/blobs/b28721136482dafb9fd2801af340364cb97e9f6eaa987908606c786045b8d334 +28 -0
- package/skills/project-update/history/blobs/b3726422d767f9219fea633bea77f0c99ad9af843c591e6e928e1ccd2a47f374 +27 -0
- package/skills/project-update/history/blobs/b3839425f9d0ab2efa42a6f5e9488ec2d26ca5fb8dcd694b8ab098e10743ca46 +32 -0
- package/skills/project-update/history/blobs/ba14a457e83f9dde65e4190fdfab83b912f7398c1442a2a1cb3d89bfac741cf3 +8 -0
- package/skills/project-update/history/blobs/bceb7189444bb6291b262ca08e34db01010dda2f6e64d696231ec3c343c95c66 +34 -0
- package/skills/project-update/history/blobs/bf82a2bb4fce1bd349ff66118930a01d61a7af8e2bd8d200c721d43afc58c713 +34 -0
- package/skills/project-update/history/blobs/c4dbeb180292a6199ad73a3be861ece07c1f8bf6bf0cc7ca204412bc8e1bb1d8 +31 -0
- package/skills/project-update/history/blobs/c644838866df2001a42501a988dd7b77aacf862bf09d22b465f3cd8a22c6b6eb +33 -0
- package/skills/project-update/history/blobs/c980d74f76a4bdfb38c94b573ac10a54eb50b850b052e15fa6f2442889b36ccb +32 -0
- package/skills/project-update/history/blobs/ceed2f3bd937f862d0e6ca59ce1b1e3603e19a0d7f85d90d6dfeb7bf690153be +29 -0
- package/skills/project-update/history/blobs/d51ea2e893482a20c5882056cf8043327ca1dc1c957927f6746929a47ee8368e +45 -0
- package/skills/project-update/history/blobs/d78f5e0bb1380259fce2cce009785a9a11f6de8b87a160080b9872c059f95060 +34 -0
- package/skills/project-update/history/blobs/d97f46a2cca020406252b1f7baa4184617fa71faca3885e2379c44ae4e1d9327 +67 -0
- package/skills/project-update/history/blobs/da3ecd4c4c414c93d04488685931df39a56bf959bfeb9c45cb1aea2c13bd991b +38 -0
- package/skills/project-update/history/blobs/e129bf71a5a922e24b2c1bc8daa8a5d2b5589b0069770e17ce17e7db5ed4e7a4 +27 -0
- package/skills/project-update/history/blobs/e504529519a371892838725f8b89b5d32e3300e345bcbf2643a3a1a48434b3dd +24 -0
- package/skills/project-update/history/blobs/e9e62d03ca70b3dffccef8dceeb214173352cfef005145e519f0e037993f1c03 +36 -0
- package/skills/project-update/history/blobs/effefbaaf4beb8f1ba55081d6b7e74bf25ebb91045dd10613418f17b95fa7182 +33 -0
- package/skills/project-update/history/blobs/f6af4581bafbf62b257d57e06c3ae2c5998aeca608ee7df037fcc3c3e4b563a9 +34 -0
- package/skills/project-update/history/blobs/fc35045777d29630303430752a651b3e553580a581036a02cc103eb96a46f4b0 +34 -0
- package/skills/project-update/history/blobs/fdc21345c77f92a42d244aa104f393f439a0ff9981a9fd462bd837e8a47931bb +48 -0
- package/skills/project-update/history/index.json +32 -13
- package/skills/project-update/render_instructions.py +12 -45
- package/skills/project-update/update.py +757 -1172
- package/skills/rr-setup/SKILL.md +42 -81
- package/skills/rr-setup/scaffold_ai.py +7 -7
- package/skills/rr-setup/setup.py +96 -164
- package/skills/rr-status/status.py +7 -10
- package/skills/usage-report/usage-report.py +4 -5
- package/skills/ai-audit/SKILL.md +0 -9
- package/skills/ai-init/SKILL.md +0 -9
- package/skills/ai-status/SKILL.md +0 -9
- package/skills/ai-task/SKILL.md +0 -9
- package/skills/project-init/SKILL.md +0 -9
- package/skills/project-update/SKILL.md +0 -9
- package/skills/project-update/adopt-map.json +0 -91
- package/skills/project-update/adopt.py +0 -2225
- package/skills/project-update/migrations/0001_schema_version.py +0 -13
- package/skills/project-update/migrations/0002_task_journal.py +0 -123
- package/skills/project-update/migrations/0003_context_diet.py +0 -65
- package/skills/project-update/migrations/0004_deterministic_gates.py +0 -57
- package/skills/project-update/migrations/0005_runtimes_and_plans.py +0 -35
- package/skills/project-update/migrations/0006_shared_files.py +0 -154
- package/skills/project-update/migrations/0007_weight_model.py +0 -295
- package/skills/project-update/migrations/0008_renames.py +0 -43
- package/skills/project-update/migrations/0009_state_file.py +0 -25
- package/skills/project-update/migrations/0010_dialog_switch.py +0 -30
- package/skills/project-update/migrations/0011_retire_risk_tiers.py +0 -37
- package/skills/project-update/migrations/README.md +0 -76
- package/skills/project-update/migrations/__init__.py +0 -186
- package/skills/sdlc-intent/SKILL.md +0 -9
- package/skills/sdlc-plan/SKILL.md +0 -9
- package/skills/sdlc-spec/SKILL.md +0 -9
- package/skills/usage-report/SKILL.md +0 -10
- /package/{ai/workflows/refactoring.md → skills/project-update/history/blobs/36cb593293496fbcf38df828a9b9b2bc0d5a53333f584a5319ebc3d799016e24} +0 -0
- /package/{ai/workflows/feature.md → skills/project-update/history/blobs/56e16072c2f3edc6c8897c690d09a11d12fc414c3e3f361f359f7dc609a21282} +0 -0
- /package/{ai/workflows/hotfix.md → skills/project-update/history/blobs/8cbefbf1aa412bae3e5019acd40b8fe0598fceba6c781906b3bc6dbd014f6b23} +0 -0
- /package/{ai/workflows/bugfix.md → skills/project-update/history/blobs/af56dc16e44ae1d8c3dac4c3ca82e2163b719553c02ea455e77430bab9a14089} +0 -0
- /package/{ai/workflows/investigation.md → skills/project-update/history/blobs/b9e09cb24958cd6831589997558974bd6356305306e8f9685e226bcdc30d60ac} +0 -0
- /package/{ai/agents/manager.md → skills/project-update/history/blobs/d0cf6388f35a92a0cf607d33909278382f6d7b6a4c32e281b9f9e0ea652c7b91} +0 -0
|
@@ -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": "
|
|
8
|
+
"version": "4.0.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": "
|
|
17
|
+
"version": "4.0.1",
|
|
18
18
|
"strict": true
|
|
19
19
|
}
|
|
20
20
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "riskrails",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.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
|
+
"version": "4.0.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/v4.0.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@
|
|
21
|
+
npx riskrails@4.0.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,83 +50,47 @@ 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
|
|
53
|
+
**Version 4.0.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
|
|
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
|
-
```bash
|
|
85
|
-
npx riskrails@3.2.1 # or: npx -y riskrails@latest --yes
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
It moves `<home>/claude-agentic/` to `<home>/riskrails/` and leaves a
|
|
89
|
-
symlink at the old path for one version, replaces the old hook entries with
|
|
90
|
-
`ai-guard.py` and `runtime-gate.py`, retires the old agents and shim files
|
|
91
|
-
to `backups/<date>/`, and rewrites the managed block. Restart the runtime.
|
|
92
|
-
In Codex, open `/hooks` and **trust the changed entries** — until you do,
|
|
93
|
-
nothing is enforced. `--dry-run` shows all of it first.
|
|
94
|
-
4. **Update each project:**
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
/rr-setup --update # dry run first; read it; then --apply
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Migrations `0007` to `0010` (and `0011`, see Upgrade to 3.1) run inside the dry run, in order, before the merge:
|
|
101
|
-
`0007` creates `.ai/policies/weight.json`, carrying over the project's
|
|
102
|
-
edits to its risk-tier table; `0008` renames the managed-block markers and
|
|
103
|
-
the router's routes; `0009` moves `current.json` to `state.json`; `0010`
|
|
104
|
-
creates `.ai/policies/dialog.json` with the dialog on. Edited markdown is
|
|
105
|
-
three-way merged, JSON policies key by key with a confirmation per key;
|
|
106
|
-
`delete?` proposals are left for you to confirm. The schema becomes 10 with a 3.0 plugin, 11 with 3.1.
|
|
107
|
-
5. **Decide two things the migrations cannot.** Keep the dialog (then amend
|
|
108
|
-
constitution C5 as `0010` hints) or set `"enabled": false`; and tune the
|
|
109
|
-
weight table — `domain.modules`, `customer_visible`, `hot_paths`,
|
|
110
|
-
`needs_step`, `irreversible` — then `state.py weight --mirror`.
|
|
111
|
-
6. **Verify**: `/rr-status` shows schema 11 and no task in flight; a
|
|
112
|
-
`/rr-build` on a small change runs light and closes on the two facts.
|
|
113
|
-
Commit `.ai/`.
|
|
114
|
-
|
|
115
|
-
Every machine of a team upgrades the plugin before a project at schema 11 is
|
|
116
|
-
committed; an older plugin refuses such a project. Rollback and the full
|
|
117
|
-
migration table are in [docs/project-update.md](docs/project-update.md); the
|
|
118
|
-
home-side details in [docs/installation.md](docs/installation.md).
|
|
56
|
+
## Upgrade to 4.0
|
|
57
|
+
|
|
58
|
+
4.0 converts nothing any more: the one-version fallbacks, the migrations and
|
|
59
|
+
the adopt command are gone, and `/rr-setup --update` regenerates a project's files from
|
|
60
|
+
the plugin. Step by step, in this order:
|
|
61
|
+
|
|
62
|
+
1. **Come from 3.1.0 or later.** 4.0 refuses a tree that still holds an old
|
|
63
|
+
shape it detects, naming the file, and regenerates any other older tree
|
|
64
|
+
without the conversions it missed. So a project last updated by an older
|
|
65
|
+
riskrails goes through the version that still converts it: install it
|
|
66
|
+
(`npx riskrails@3.1.0`), run `/rr-setup --update` with it, then install 4.0.
|
|
67
|
+
2. **Commit every project's `.ai/`** first; `git restore` is the rollback.
|
|
68
|
+
3. **Update the home** on every machine, for every runtime: `npx riskrails@4.0.1`
|
|
69
|
+
(the plugin route and the npm route are separate installs: reinstall both
|
|
70
|
+
if you use both). Restart the runtime. In Codex, open `/hooks` and trust the
|
|
71
|
+
changed entries.
|
|
72
|
+
4. **Run `/rr-setup --update` in each project right away.** Until you do, the
|
|
73
|
+
project's old shapes are not read: the weight lacks its `domain.modules`, no
|
|
74
|
+
`verify_command` is found, and a task in the old state file is not scope-guarded.
|
|
75
|
+
Dry run first, then `--apply`; a `regenerate?` or `delete?` row is confirmed
|
|
76
|
+
by you in a terminal of your own.
|
|
77
|
+
5. **Check**: `/rr-status` shows schema 12 and no task in flight.
|
|
78
|
+
|
|
79
|
+
The alias commands (the `/ai-*`, `/sdlc-*`, `/project-*` and usage-report names)
|
|
80
|
+
are removed; use the `/rr-*` commands. Details are in
|
|
81
|
+
[docs/project-update.md](docs/project-update.md), the home-side ones in
|
|
82
|
+
[docs/installation.md](docs/installation.md).
|
|
119
83
|
|
|
120
84
|
## Install
|
|
121
85
|
|
|
122
86
|
```bash
|
|
123
|
-
npx riskrails@
|
|
124
|
-
npx riskrails@
|
|
125
|
-
npx riskrails@
|
|
126
|
-
npx riskrails@
|
|
127
|
-
npx riskrails@
|
|
128
|
-
npx riskrails@
|
|
129
|
-
npx riskrails@
|
|
87
|
+
npx riskrails@4.0.1 # detects Claude Code, Codex, or both; proposes the detected plan
|
|
88
|
+
npx riskrails@4.0.1 --target codex # auto | claude | codex | both
|
|
89
|
+
npx riskrails@4.0.1 --plan max --fable no # pro | team-pro | team-max | max | max20 | balanced-max
|
|
90
|
+
npx riskrails@4.0.1 --codex-plan plus # plus | pro | balanced-max
|
|
91
|
+
npx riskrails@4.0.1 --context economy # the plan's compaction window; the context guard also blocks
|
|
92
|
+
npx riskrails@4.0.1 --statusline own # keep your own statusline command (from 3.2.1; default: the plugin's two-line statusline)
|
|
93
|
+
npx riskrails@4.0.1 --dry-run # print what would be written, write nothing
|
|
130
94
|
npx -y riskrails@latest --yes # update in place, keeping the recorded plan
|
|
131
95
|
```
|
|
132
96
|
|
|
@@ -145,7 +109,6 @@ The same commands in both runtimes:
|
|
|
145
109
|
| You want to | Run |
|
|
146
110
|
|---|---|
|
|
147
111
|
| set up `.ai/` in a project, or bring it up to the installed plugin | `/rr-setup`, `/rr-setup --update` |
|
|
148
|
-
| move another AI tool's files, or the project's own toolbox, into this layout | `/rr-setup --adopt` |
|
|
149
112
|
| score the repository against the twelve plays | `/rr-setup --audit` |
|
|
150
113
|
| turn an idea into intent, requirements and design | `/rr-spec <idea>` |
|
|
151
114
|
| plan a spec into tasks, or one task into a mode and a start | `/rr-plan <spec or task>` |
|
|
@@ -155,10 +118,6 @@ The same commands in both runtimes:
|
|
|
155
118
|
| close the task, get the human's approval | `/rr-ship` |
|
|
156
119
|
| see where the task stands, or what it cost | `/rr-status`, `/rr-status --cost` |
|
|
157
120
|
|
|
158
|
-
The old names — `/ai-init`, `/ai-task`, `/ai-status`, `/ai-audit`,
|
|
159
|
-
`/project-init`, `/project-update`, `/sdlc-intent`, `/sdlc-spec`,
|
|
160
|
-
`/sdlc-plan`, `/usage-report` — are aliases for one version.
|
|
161
|
-
|
|
162
121
|
### Three modes
|
|
163
122
|
|
|
164
123
|
`state.py weight` scores six axes 0–2 — scope, behaviour, reversibility,
|
|
@@ -265,7 +224,6 @@ to do: [docs/known-risks.md](docs/known-risks.md).
|
|
|
265
224
|
| Codex facts are observed, not guaranteed | grep the journals for `missed_reroute` after a Codex upgrade |
|
|
266
225
|
| The dialog switch is read in two places | when the dialog and the terminal disagree, use the terminal |
|
|
267
226
|
| Budgets and quota are advice, not limits | read the quota with its `stale` field |
|
|
268
|
-
| `--adopt` edge cases fail safe, not smoothly | adopt on a committed tree, keep `original/` |
|
|
269
227
|
| The usage report's parse cache trusts mtime and size | `--no-cache` when a number surprises you |
|
|
270
228
|
|
|
271
229
|
## Tests
|
|
@@ -276,15 +234,14 @@ python3 -I -m unittest discover -s tests -p 'test_*.py' # CI's serial oracl
|
|
|
276
234
|
AI_E2E=1 python3 -I -m unittest discover -s tests -p test_end_to_end.py
|
|
277
235
|
```
|
|
278
236
|
|
|
279
|
-
Fifty-
|
|
237
|
+
Fifty-eight suites, 1133 tests, about 75 seconds in parallel. The guards against JSON fixtures for both runtimes and a
|
|
280
238
|
golden of 1 341 recorded decisions, byte for byte, with timed cases that hold
|
|
281
239
|
the hot path under its budget; the state machine, the weight, the batch loop,
|
|
282
240
|
the dialog contract and the documents rule; the compact record and its
|
|
283
241
|
pre-push validator; the installer for every plan and `--target`, the Codex
|
|
284
242
|
agent renderer and the `config.toml` merge against a fixture full of
|
|
285
243
|
third-party settings; `/rr-setup --update` against projects scaffolded from
|
|
286
|
-
the oldest shipped templates and edited by hand;
|
|
287
|
-
Kiro and Cursor fixtures; and an end-to-end run that installs both runtimes
|
|
244
|
+
the oldest shipped templates and edited by hand; and an end-to-end run that installs both runtimes
|
|
288
245
|
into scratch directories, drives one task through the guards from Claude and
|
|
289
246
|
then from Codex, and proves both see the same state. No suite reads or writes
|
|
290
247
|
the developer's real `~/.claude` or `~/.codex`.
|
|
@@ -295,7 +252,7 @@ the developer's real `~/.claude` or `~/.codex`.
|
|
|
295
252
|
|---|---|
|
|
296
253
|
| [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 |
|
|
297
254
|
| [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 |
|
|
298
|
-
| [docs/project-update.md](docs/project-update.md) | shared versus project-owned files, how a file is
|
|
255
|
+
| [docs/project-update.md](docs/project-update.md) | shared versus project-owned files, how a file is regenerated, what is kept, the rollback |
|
|
299
256
|
| [docs/architecture.md](docs/architecture.md) | how the pieces fit: the installed home, `.ai/`, the pipeline, the state, the hooks, the two adapters |
|
|
300
257
|
| [docs/weight.md](docs/weight.md) | the six axes, the three modes, what is measured and what is declared, tuning the table |
|
|
301
258
|
| [docs/workflows.md](docs/workflows.md) | feature, bugfix, refactoring, hotfix, investigation |
|
|
@@ -54,7 +54,7 @@ in light and guarded, about 80 in full, then FINDINGS and VERDICT, with
|
|
|
54
54
|
|
|
55
55
|
## Boundary
|
|
56
56
|
|
|
57
|
-
The brief names the task's mode; `.ai/state/state.json`
|
|
57
|
+
The brief names the task's mode; `.ai/state/state.json` has it otherwise. In
|
|
58
58
|
light and guarded mode your inputs are the task goal, the diff, the tests the
|
|
59
59
|
step names, and the project's own fixture corpus and threat model:
|
|
60
60
|
`.ai/policies/safety.md`, the guards' `*-defaults.json`, the test fixtures.
|
package/ai/using-riskrails.md
CHANGED
|
@@ -8,7 +8,6 @@ the steps; `policies/` holds the rules; this says which to open.
|
|
|
8
8
|
| You want to | Run |
|
|
9
9
|
|---|---|
|
|
10
10
|
| set up `.ai/` in a project, or bring it up to the installed plugin | `/rr-setup`, `/rr-setup --update` |
|
|
11
|
-
| move another AI tool's files into this layout | `/rr-setup --adopt` |
|
|
12
11
|
| score the repository against the twelve plays | `/rr-setup --audit` |
|
|
13
12
|
| turn an idea into intent, requirements and design | `/rr-spec <idea>` |
|
|
14
13
|
| plan a spec into tasks, or one task into a mode and a start | `/rr-plan <spec or task>` |
|
|
@@ -18,8 +17,6 @@ the steps; `policies/` holds the rules; this says which to open.
|
|
|
18
17
|
| close the task, get the human's approval | `/rr-ship` |
|
|
19
18
|
| see where the task stands, or what it cost | `/rr-status`, `/rr-status --cost` |
|
|
20
19
|
|
|
21
|
-
The old names (`/ai-task`, `/ai-init`, `/sdlc-spec` …) stay one version as aliases.
|
|
22
|
-
|
|
23
20
|
## Three modes
|
|
24
21
|
|
|
25
22
|
`state.py weight` scores six axes 0–2 — scope, behaviour, reversibility, domain,
|
package/bin/riskrails.js
CHANGED
|
@@ -152,7 +152,6 @@ const exists = (p) => fs.existsSync(p); // follows symlink
|
|
|
152
152
|
const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch (e) { return false; } };
|
|
153
153
|
const isFile = (p) => { try { return fs.statSync(p).isFile(); } catch (e) { return false; } };
|
|
154
154
|
const isLink = (p) => { try { return fs.lstatSync(p).isSymbolicLink(); } catch (e) { return false; } };
|
|
155
|
-
const lexists = (p) => { try { fs.lstatSync(p); return true; } catch (e) { return false; } };
|
|
156
155
|
const read = (p) => fs.readFileSync(p, 'utf8');
|
|
157
156
|
const readJSON = (p) => JSON.parse(read(p));
|
|
158
157
|
const pretty = (v) => JSON.stringify(v, null, 2);
|
|
@@ -311,7 +310,7 @@ function renderStub(scope, runtime, dst, vars, kind = 'block') {
|
|
|
311
310
|
// block_size — what the always-loaded block costs, and how much of the file is the user's own.
|
|
312
311
|
function blockSize(file, budget) {
|
|
313
312
|
const text = read(file);
|
|
314
|
-
const m = text.match(/<!--
|
|
313
|
+
const m = text.match(/<!-- riskrails:start -->[\s\S]*?<!-- riskrails:end -->/);
|
|
315
314
|
const block = m ? Buffer.byteLength(m[0]) : 0;
|
|
316
315
|
out(`${path.basename(file)}: managed block ${spaced(block)} B (budget ${spaced(budget)}), file ${spaced(Buffer.byteLength(text))} B — the rest is yours`);
|
|
317
316
|
}
|
|
@@ -358,12 +357,10 @@ function writeProfile(home, profile) {
|
|
|
358
357
|
}
|
|
359
358
|
|
|
360
359
|
function prevPlan(home) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
} catch (e) { /* next */ }
|
|
366
|
-
}
|
|
360
|
+
try {
|
|
361
|
+
const plan = readJSON(path.join(home, 'riskrails', 'profile.json')).plan;
|
|
362
|
+
return plan === null || plan === undefined || plan === false ? '' : String(plan);
|
|
363
|
+
} catch (e) { /* none */ }
|
|
367
364
|
return '';
|
|
368
365
|
}
|
|
369
366
|
|
|
@@ -748,15 +745,13 @@ function claudeDryRun() {
|
|
|
748
745
|
if (C.TIER === 'max') out(` bin: claude-1m (opus[1m]${FABLE === 'yes' ? ' / fable[1m]' : ''}, pins the model at launch; compacts near ${C.ONE_M_AT})`);
|
|
749
746
|
const plugin = claudePlugin(CLAUDE_DIR);
|
|
750
747
|
skillsLine(plugin);
|
|
751
|
-
out(' retire: skills/status
|
|
748
|
+
out(' retire: skills/{status,ai-status,sdlc-intent,sdlc-plan,sdlc-spec}, into backups/<date>/ if present');
|
|
752
749
|
shimLine('settings.json');
|
|
753
750
|
pluginLine(plugin, true);
|
|
754
|
-
homeLine(CLAUDE_DIR);
|
|
755
751
|
sharedLine();
|
|
756
752
|
out(' config: ai-git-guard.json (only if absent)');
|
|
757
753
|
out(' routing: riskrails/routing.md');
|
|
758
754
|
out(' profile: riskrails/profile.json');
|
|
759
|
-
out('== would migrate: the claude-routing managed block, if present, into this one');
|
|
760
755
|
}
|
|
761
756
|
|
|
762
757
|
// Every backup goes to one dated directory, <root>/backups/<date>/<path>: a backup beside
|
|
@@ -780,18 +775,6 @@ function retire(root, rel) { // move what nothing installs
|
|
|
780
775
|
out(`retired: ${rel} -> backups/${BACKUP_DATE}/${rel}`);
|
|
781
776
|
}
|
|
782
777
|
|
|
783
|
-
// move_home — R35: the install home was <root>/claude-agentic/ before 3.4b. It moves to
|
|
784
|
-
// <root>/riskrails/ and the old path becomes a symlink to it for one version. A real old
|
|
785
|
-
// home beside an existing new one (or a link there, dangling or not) is retired.
|
|
786
|
-
function moveHome(root) {
|
|
787
|
-
const old = path.join(root, 'claude-agentic'), nw = path.join(root, 'riskrails');
|
|
788
|
-
if (!isDir(old) || isLink(old)) return;
|
|
789
|
-
if (lexists(nw)) retire(root, 'claude-agentic');
|
|
790
|
-
else { fs.renameSync(old, nw); out('moved: claude-agentic/ -> riskrails/'); }
|
|
791
|
-
fs.symlinkSync('riskrails', old);
|
|
792
|
-
out('linked: claude-agentic -> riskrails (for one version)');
|
|
793
|
-
}
|
|
794
|
-
|
|
795
778
|
// sweep_bak — every *.bak an older installer left beside a live file moves into
|
|
796
779
|
// backups/<date>/, keeping its path. Only the directories and root files this installer writes.
|
|
797
780
|
function sweepBak(root, ...names) {
|
|
@@ -808,6 +791,8 @@ function sweepBak(root, ...names) {
|
|
|
808
791
|
}
|
|
809
792
|
}
|
|
810
793
|
|
|
794
|
+
const RETIRED_SKILLS = ['status', 'ai-status', 'sdlc-intent', 'sdlc-plan', 'sdlc-spec'];
|
|
795
|
+
|
|
811
796
|
// The seven agents the 2026-09-02 install shipped, and the nine redesign 2.4 removed (with
|
|
812
797
|
// the Codex -strong/-balanced twins). Moved, not deleted: one mv out of backups/ restores one.
|
|
813
798
|
const STALE_AGENTS = ['code-implementer', 'qa-runner', 'qa-fixer', 'db-migrator', 'doc-lookup', 'log-inspector',
|
|
@@ -831,8 +816,8 @@ function installFile(src, dst, root = CLAUDE_DIR) { // back up on a real change
|
|
|
831
816
|
// their scripts (state.py, ai-path, ...) through $AI_HOME/skills/ as on the npx route.
|
|
832
817
|
function installSkills(root, plugin) {
|
|
833
818
|
const backups = path.join(root, 'backups', BACKUP_DATE, 'skills');
|
|
834
|
-
// /status became /rr-status (
|
|
835
|
-
retire(root,
|
|
819
|
+
// /status became /rr-status (2.3) and the ai-status and sdlc-* aliases ended in 4.0: a leftover would load as a second command.
|
|
820
|
+
for (const n of RETIRED_SKILLS) retire(root, `skills/${n}`);
|
|
836
821
|
for (const name of ls(path.join(SRC, 'skills')).filter((n) => isDir(path.join(SRC, 'skills', n)))) {
|
|
837
822
|
const d = path.join(SRC, 'skills', name), dst = path.join(root, 'skills', name);
|
|
838
823
|
// A developer who ran the scripts in place leaves __pycache__ behind; it is not copied.
|
|
@@ -879,43 +864,15 @@ function installShared(root) {
|
|
|
879
864
|
installFile(path.join(SRC, 'scripts/resolve-profile.py'), path.join(home, 'scripts/resolve-profile.py'), root);
|
|
880
865
|
}
|
|
881
866
|
|
|
882
|
-
function homeLine(root) {
|
|
883
|
-
const old = path.join(root, 'claude-agentic');
|
|
884
|
-
if (isDir(old) && !isLink(old)) out(' home: claude-agentic/ -> riskrails/, the old path a symlink for one version');
|
|
885
|
-
}
|
|
886
867
|
const sharedLine = () => out(` shared: riskrails/ai (${countFiles(path.join(SRC, 'ai'))} files), riskrails/ai-path`);
|
|
887
868
|
|
|
888
|
-
// managed_block — replace the one managed block, or append it
|
|
889
|
-
// plugin's block and an unmarked legacy section on the way. Backs up a non-empty target first.
|
|
869
|
+
// managed_block — replace the one managed block, or append it. Backs up a non-empty target first.
|
|
890
870
|
function managedBlock(target, blockFile, label) {
|
|
891
871
|
if (!exists(target)) fs.writeFileSync(target, '');
|
|
892
872
|
if (fs.statSync(target).size > 0) backupFile(target, path.dirname(target));
|
|
893
873
|
const block = read(blockFile).replace(/^\n+|\n+$/g, '');
|
|
894
874
|
let text = read(target);
|
|
895
875
|
const start = '<!-- riskrails:start -->', end = '<!-- riskrails:end -->';
|
|
896
|
-
const notes = [];
|
|
897
|
-
const routing = text.match(/<!-- claude-routing:start -->[\s\S]*?<!-- claude-routing:end -->\n?/);
|
|
898
|
-
if (routing) {
|
|
899
|
-
text = text.slice(0, routing.index) + text.slice(routing.index + routing[0].length);
|
|
900
|
-
notes.push('migrated the claude-routing block');
|
|
901
|
-
}
|
|
902
|
-
const legacy = text.match(/^# Model allocation by task and scope[\s\S]*?(?=^# |(?![\s\S]))/m);
|
|
903
|
-
if (legacy && !text.slice(0, legacy.index).includes(start)) {
|
|
904
|
-
text = text.slice(0, legacy.index) + text.slice(legacy.index + legacy[0].length);
|
|
905
|
-
notes.push("migrated an unmarked 'Model allocation' section");
|
|
906
|
-
}
|
|
907
|
-
// A legacy claude-agentic (before 3.4b) or Codex-agentic pair is the managed block under its
|
|
908
|
-
// old name: rename it, never append beside it.
|
|
909
|
-
if (!text.includes(start) && !text.includes(end)) {
|
|
910
|
-
const pair = text.match(/<!-- (claude-agentic|[Cc]odex-agentic):start -->[\s\S]*?<!-- \1:end -->/);
|
|
911
|
-
if (pair) {
|
|
912
|
-
const name = pair[1];
|
|
913
|
-
text = text.slice(0, pair.index)
|
|
914
|
-
+ pair[0].replace(`<!-- ${name}:start -->`, start).replace(`<!-- ${name}:end -->`, end)
|
|
915
|
-
+ text.slice(pair.index + pair[0].length);
|
|
916
|
-
notes.push(`renamed the legacy ${name} markers`);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
876
|
let nw, action;
|
|
920
877
|
if (text.includes(start) && text.includes(end)) {
|
|
921
878
|
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -928,7 +885,7 @@ function managedBlock(target, blockFile, label) {
|
|
|
928
885
|
}
|
|
929
886
|
nw = nw.replace(/\n{4,}/g, '\n\n\n');
|
|
930
887
|
fs.writeFileSync(target, nw);
|
|
931
|
-
out(`${label}: ${action}
|
|
888
|
+
out(`${label}: ${action}`);
|
|
932
889
|
}
|
|
933
890
|
|
|
934
891
|
// The three shell guards 3.5 retired: ai-guard.py runs all of them in one process. Their
|
|
@@ -1114,7 +1071,6 @@ const loadJSONFile = (file) => JSON.parse(read(file));
|
|
|
1114
1071
|
const writeJSONFile = (file, doc) => { fs.writeFileSync(file + '.tmp', pretty(doc) + '\n'); fs.renameSync(file + '.tmp', file); };
|
|
1115
1072
|
|
|
1116
1073
|
function claudeApply() {
|
|
1117
|
-
moveHome(CLAUDE_DIR);
|
|
1118
1074
|
for (const d of ['agents', 'hooks/lib', 'hooks/guard', 'skills']) mkdirp(path.join(CLAUDE_DIR, d));
|
|
1119
1075
|
sweepBak(CLAUDE_DIR, 'settings.json', 'CLAUDE.md');
|
|
1120
1076
|
|
|
@@ -1390,10 +1346,9 @@ function codexDryRun() {
|
|
|
1390
1346
|
out(` hooks: ${codexHookFiles().map((f) => path.basename(f)).join(',')}`);
|
|
1391
1347
|
const plugin = codexPlugin(CODEX_DIR);
|
|
1392
1348
|
skillsLine(plugin);
|
|
1393
|
-
out(' retire: skills/status
|
|
1349
|
+
out(' retire: skills/{status,ai-status,sdlc-intent,sdlc-plan,sdlc-spec}, into backups/<date>/ if present');
|
|
1394
1350
|
shimLine('hooks.json');
|
|
1395
1351
|
pluginLine(plugin, true);
|
|
1396
|
-
homeLine(CODEX_DIR);
|
|
1397
1352
|
sharedLine();
|
|
1398
1353
|
out(' config: ai-git-guard.json (only if absent)');
|
|
1399
1354
|
out(' routing: riskrails/routing.md');
|
|
@@ -1404,7 +1359,6 @@ function codexDryRun() {
|
|
|
1404
1359
|
}
|
|
1405
1360
|
|
|
1406
1361
|
function codexApply() {
|
|
1407
|
-
moveHome(CODEX_DIR);
|
|
1408
1362
|
for (const d of ['agents', 'hooks/lib', 'hooks/guard', 'skills']) mkdirp(path.join(CODEX_DIR, d));
|
|
1409
1363
|
sweepBak(CODEX_DIR, 'hooks.json', 'config.toml', 'AGENTS.md');
|
|
1410
1364
|
|
|
@@ -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. Also the installer's statusline record (.claude/riskrails/statusline.json), which keeps the statusline command it replaced.
|
|
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. 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)$",
|
package/hooks/context-guard.py
CHANGED
|
@@ -284,7 +284,7 @@ def prompt_text(message):
|
|
|
284
284
|
text = REMINDER.sub("", content).strip()
|
|
285
285
|
# only a wrapper at the very start is a slash command: a prompt may quote these tags
|
|
286
286
|
command = COMMAND.search(text) if text.startswith(("<command-name>", "<command-message>")) else None
|
|
287
|
-
if command: # "/
|
|
287
|
+
if command: # "/rr-build fix the export" is an instruction; a bare "/model" is not
|
|
288
288
|
name, args = command.group(1).strip(), (COMMAND_ARGS.search(text) or [None, ""])[1].strip()
|
|
289
289
|
return f"{name} {args}" if args else None
|
|
290
290
|
# local command output, caveats, interruptions
|
|
@@ -360,7 +360,7 @@ def ai_root(cwd):
|
|
|
360
360
|
"""The project the session is in, or None.
|
|
361
361
|
|
|
362
362
|
The walk stops at the repository it starts in, and never leaves $HOME. An
|
|
363
|
-
unbounded walk would let one `/
|
|
363
|
+
unbounded walk would let one `/rr-setup` in a home directory adopt every
|
|
364
364
|
session on the machine: a nested, unrelated checkout would have its prompts
|
|
365
365
|
recorded into the ancestor's session.json and be handed the ancestor's
|
|
366
366
|
handoff at startup. cwd falls back to the process's own, which is where the
|
|
@@ -390,8 +390,6 @@ def task_in_flight(root):
|
|
|
390
390
|
if not root:
|
|
391
391
|
return False
|
|
392
392
|
state = os.path.join(root, ".ai", "state", "state.json")
|
|
393
|
-
if not os.path.isfile(state):
|
|
394
|
-
state = os.path.join(root, ".ai", "state", "current.json") # R34: legacy name, one version
|
|
395
393
|
try:
|
|
396
394
|
with open(state, encoding="utf-8") as fh:
|
|
397
395
|
return (json.load(fh) or {}).get("current_stage") != "done"
|
package/hooks/guard/paths.py
CHANGED
|
@@ -672,9 +672,8 @@ class PathGuard: # pylint: disable=too-many-instance-
|
|
|
672
672
|
# -------------------------------------------------------------- the task
|
|
673
673
|
|
|
674
674
|
def _state_file(self):
|
|
675
|
-
"""
|
|
676
|
-
|
|
677
|
-
return new if is_file(new) else self.root + "/.ai/state/current.json"
|
|
675
|
+
"""The task's state file."""
|
|
676
|
+
return self.root + "/.ai/state/state.json"
|
|
678
677
|
|
|
679
678
|
def _task_line(self):
|
|
680
679
|
loaded = load_json_file(self._state_file())
|
|
@@ -720,9 +719,7 @@ class PathGuard: # pylint: disable=too-many-instance-
|
|
|
720
719
|
if doc is not None and not isinstance(doc, dict):
|
|
721
720
|
continue
|
|
722
721
|
for tag, key in (("d", "deny_patterns"), ("a", "allow_patterns"),
|
|
723
|
-
("p", "protected_config_patterns"), ("k", "task_protected_patterns")
|
|
724
|
-
# A project's list from before 3.4 keeps its read and write deny.
|
|
725
|
-
("d", "dependency_instruction_patterns")):
|
|
722
|
+
("p", "protected_config_patterns"), ("k", "task_protected_patterns")):
|
|
726
723
|
v = doc.get(key) if doc else None
|
|
727
724
|
for item in jq_iter(v if v is not None and v is not False else []):
|
|
728
725
|
if is_jq_string(item) and item != "":
|
package/hooks/guard/scope.py
CHANGED
|
@@ -3,7 +3,7 @@ MultiEdit, NotebookEdit and Codex's apply_patch.
|
|
|
3
3
|
|
|
4
4
|
During the implementation stage the approved plan names the files a step may
|
|
5
5
|
touch; an edit outside them is refused with SCOPE_CHANGE_REQUIRED. Inert unless
|
|
6
|
-
.ai/state/state.json
|
|
6
|
+
.ai/state/state.json exists and
|
|
7
7
|
its stage is "implementation".
|
|
8
8
|
|
|
9
9
|
Bash semantics kept: a plan glob is matched as `[[ $rel == $pattern ]]` — "*"
|
|
@@ -334,8 +334,6 @@ def check(ctx):
|
|
|
334
334
|
if root is None:
|
|
335
335
|
return
|
|
336
336
|
state = root + "/.ai/state/state.json"
|
|
337
|
-
if not is_file(state):
|
|
338
|
-
state = root + "/.ai/state/current.json"
|
|
339
337
|
if not is_file(state):
|
|
340
338
|
return
|
|
341
339
|
step_id, task_id, allowed, forbidden, reason = read_state(state)
|