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.
Files changed (81) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/README.md +246 -812
  5. package/agents/ai-reviewer.md.tmpl +6 -4
  6. package/ai/policies/review-economy.md +7 -2
  7. package/ai/using-riskrails.md +15 -0
  8. package/bin/claude-1m +15 -14
  9. package/bin/riskrails.js +108 -32
  10. package/codex/hooks.json +25 -2
  11. package/hooks/AGENTS.md +2 -2
  12. package/hooks/CLAUDE.md +2 -2
  13. package/hooks/ai-guard.py +29 -8
  14. package/hooks/ai-path-guard-defaults.json +3 -1
  15. package/hooks/codex-hooks.json +25 -2
  16. package/hooks/context-guard.py +251 -35
  17. package/hooks/dialog-gate.py +105 -0
  18. package/hooks/guard/common.py +5 -0
  19. package/hooks/guard/decision.py +123 -0
  20. package/hooks/guard/dialog.py +598 -0
  21. package/hooks/guard/git.py +49 -3
  22. package/hooks/guard/paths.py +62 -3
  23. package/hooks/hooks.json +37 -2
  24. package/hooks/memory-pre-push.py +28 -0
  25. package/hooks/runtime-gate.py +59 -12
  26. package/hooks/update_notice.py +135 -0
  27. package/instructions/routing.md +4 -4
  28. package/instructions/stub.md +2 -2
  29. package/package.json +1 -1
  30. package/profiles/max.json +3 -1
  31. package/profiles/pro.json +3 -1
  32. package/scripts/merge-codex-config.py +134 -0
  33. package/scripts/resolve-profile.py +15 -3
  34. package/settings.common.json +39 -2
  35. package/skills/AGENTS.md +2 -2
  36. package/skills/CLAUDE.md +2 -2
  37. package/skills/ai-init/templates/.ai/AGENTS.md +2 -2
  38. package/skills/ai-init/templates/.ai/VERSION +1 -1
  39. package/skills/ai-init/templates/.ai/policies/dialog.json +1 -0
  40. package/skills/ai-init/templates/.ai/policies/safety.md +22 -2
  41. package/skills/ai-init/templates/.ai/policies/weight.json +1 -0
  42. package/skills/ai-init/templates/.ai/policies/weight.md +2 -2
  43. package/skills/ai-init/templates/.ai/state/README.md +9 -6
  44. package/skills/ai-init/templates/AGENTS.block.md +1 -1
  45. package/skills/ai-init/templates/CLAUDE.block.md +1 -1
  46. package/skills/ai-init/templates/GEMINI.block.md +1 -1
  47. package/skills/ai-init/templates/junie-guidelines.block.md +1 -1
  48. package/skills/ai-task/memory_compact.py +968 -0
  49. package/skills/ai-task/memory_format.py +775 -0
  50. package/skills/ai-task/memory_store.py +528 -0
  51. package/skills/ai-task/memory_sync.py +1571 -0
  52. package/skills/ai-task/sensors.py +2 -2
  53. package/skills/ai-task/state.py +967 -70
  54. package/skills/ai-task/weight.py +3 -3
  55. package/skills/project-init/templates/constitution.md +1 -1
  56. package/skills/project-update/history/blobs/164cd34c182de88c33de7df35beb29200d792419e1c13a5f0b2104f944e57be7 +1 -0
  57. package/skills/project-update/history/blobs/1d1d907449ec74594d0ee923ac142ae746b9aedf0a1eda9469f075a44d23e3d7 +13 -0
  58. package/skills/project-update/history/blobs/23a42febbacaa7b52a802d428033a99c0694391c81d51fe3b6e1dff608a641d5 +67 -0
  59. package/skills/project-update/history/blobs/5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 +49 -0
  60. package/skills/project-update/history/blobs/5f65bc7ab3fc3c4e75ae05d49eaf4331fafc398c661abe33c69f7380a720344c +32 -0
  61. package/skills/project-update/history/blobs/7be527cd648b27cca6e267de30f8e33a2ce17d5631a82694f23ae5c0623efe1f +15 -0
  62. package/skills/project-update/history/blobs/7ee99edafe7cec45ff577376c0d0178fdb2690134d4656488f46e129aba3cc5a +67 -0
  63. package/skills/project-update/history/blobs/8b3ef9586491a426767c01da5d01bdd6b0655c576c68c4cc6dfb093367817dc0 +67 -0
  64. package/skills/project-update/history/blobs/917df3320d778ddbaa5c5c7742bc4046bf803c36ed2b050f30844ed206783469 +1 -0
  65. package/skills/project-update/history/blobs/935af1019931058ec3a0b834fe230e127ec914f75edcc4a6c589cfe733b0b979 +13 -0
  66. package/skills/project-update/history/blobs/ae5ce131d6bae401199ff32d8c3dc4d8aec188010a9111624ac64506e2116ff4 +84 -0
  67. package/skills/project-update/history/blobs/ca4499d829268c8766fb3b825a515e88dc55c237e9d202816b50959b4dac031f +13 -0
  68. package/skills/project-update/history/blobs/e9adb7a9905eb719ac78ab96671f055da53e5001810f5ad6d6c94306b0323fe7 +74 -0
  69. package/skills/project-update/history/blobs/fae3064e648cdc55be4aeb1177525b302a5e34ebd752e4a904f47dc0f4557b96 +71 -0
  70. package/skills/project-update/history/index.json +32 -13
  71. package/skills/project-update/migrations/0009_state_file.py +25 -0
  72. package/skills/project-update/migrations/0010_dialog_switch.py +30 -0
  73. package/skills/project-update/migrations/README.md +2 -1
  74. package/skills/project-update/update.py +12 -3
  75. package/skills/rr-build/SKILL.md +14 -5
  76. package/skills/rr-build-all/SKILL.md +190 -0
  77. package/skills/rr-plan/SKILL.md +13 -3
  78. package/skills/rr-review/SKILL.md +18 -8
  79. package/skills/rr-ship/SKILL.md +16 -8
  80. package/skills/rr-status/SKILL.md +2 -2
  81. package/skills/usage-report/usage-report.py +41 -8
@@ -294,7 +294,7 @@ def mirror(path):
294
294
  (policy.get("rules") or {}).get("hint_raises_by", 1), h.get("note", ""))
295
295
  for h in policy.get("hints") or []]
296
296
  out += ["", "Rules: " + json.dumps(policy.get("rules") or {}, ensure_ascii=False), "",
297
- "Budgets: review_rounds_max %s · remediation_rounds %s · tests %s" % (
298
- policy.get("review_rounds_max"), policy.get("remediation_rounds"),
299
- json.dumps(policy.get("tests") or {}))]
297
+ "Budgets: review_rounds_max %s · remediation_rounds %s · tests %s%s" % (
298
+ policy.get("review_rounds_max"), policy.get("remediation_rounds"), json.dumps(policy.get("tests") or {}),
299
+ " · batch %s" % json.dumps(policy["batch"]) if isinstance(policy.get("batch"), dict) else "")]
300
300
  return "\n".join(out) + "\n"
@@ -7,7 +7,7 @@ C1. Production behaviour is the source of truth; what looks wrong may be load-be
7
7
  C2. A bugfix starts with the failing test; legacy behaviour gets a characterization test before it changes.
8
8
  C3. A refactoring changes no behaviour and is never mixed with a feature.
9
9
  C4. Every step names its files; scope grows only by amending the plan.
10
- C5. No agent commits, merges or deploys; a human approves outside the agent.
10
+ C5. No agent merges or deploys; an agent commits, pushes or approves only by running, verbatim and once, a command a human picked in the dialog; otherwise a human runs it outside the agent.
11
11
  C6. <!-- project principle — filled by /ai-init from the survey, confirmed by you -->
12
12
  C7. <!-- project principle -->
13
13
  C8. <!-- project principle -->
@@ -0,0 +1 @@
1
+ { "version": 1, "enabled": true, "window_seconds": 120, "stop_blocks_max": 2 }
@@ -0,0 +1,13 @@
1
+ <!-- riskrails:start -->
2
+ ## AI agent workflow
3
+
4
+ This repository runs an agentic pipeline under `.ai/`. Read `.ai/AGENTS.md` first: it routes to the policies, workflows and rules, which load on demand; `.ai/policies/` is binding.
5
+
6
+ - Production behaviour is the source of truth: document problems outside the task, do not fix them.
7
+
8
+ - A change runs through `/rr-build <request>`; `/rr-status` shows where it stands. Each step names the files it may touch — an edit outside them is refused: answer `SCOPE_CHANGE_REQUIRED`.
9
+
10
+ - Verify before reporting done: `verify_command` from `.ai/policies/testing.md` once, to the end, then `e2e_command` once; every failure fixed as one batch; show the output.
11
+ - No agent merges or deploys; commit, push, approval only as the dialog picks, else a human outside. When a review flags the same mistake twice, the correction goes into this file.
12
+ - Files, not chat, carry decisions: `.ai/reports/<task-id>/task.md` (answer by filling `[Answer]:`), `.ai/state/handoff.md` (read first when resuming), `docs/sdlc/constitution.md` (this project's principles).
13
+ <!-- riskrails:end -->
@@ -0,0 +1,67 @@
1
+ # Task state
2
+
3
+ `current.json` is the machine-readable record of the task in flight. It is
4
+ written by `skills/ai-task/state.py` (atomically), read by `/ai-status`, and
5
+ enforced by `ai-scope-guard`, which derives the files the current step may touch
6
+ from it.
7
+
8
+ Two other writers exist, and only these two. A schema migration in
9
+ `/project-update` may change the state through `ctx.patch_state()`, with the
10
+ same atomic write and a `schema_migrated` history entry — that is how a task in
11
+ flight survives a change to the shape of this file. And `hooks/context-guard.py`
12
+ writes `session.json`, the sidecar beside this file, and nothing else: it never
13
+ touches `current.json`. Nothing else writes any of it — an agent never edits
14
+ them by hand, and the path guard refuses when one tries.
15
+
16
+ It is **not committed**: it describes a session, not the repository. The
17
+ `.gitignore` entry added by `/ai-init` keeps it out of git.
18
+
19
+ When a task closes, `state.py close` writes the final state to
20
+ `.ai/state/logs/<task-id>/state.json`, beside the task's verify logs — local,
21
+ **not** committed. What is committed is `.ai/reports/<task-id>/events.jsonl`
22
+ and `task.md`, and in packages mode the task's compact record under
23
+ `.ai/memory/` — that is the audit trail: which stages ran, in what order, who
24
+ approved what, and when (the plugin's `docs/local-memory.md`).
25
+
26
+ It holds facts, never transcripts:
27
+
28
+ ```
29
+ task_id, goal, workflow, risk_tier, current_stage, affected_modules,
30
+ context_summary_ref, approved_plan { ref, current_step_id, steps[] },
31
+ completed_steps, test_status, e2e_status, review_status, security_status, open_risks,
32
+ next_action, owner_runtime, resume_point { stage, step_id, next_action, at, runtime },
33
+ questions { file, pending[] }, handoff { file, written_at, reason },
34
+ human_approval { required, granted, granted_by, granted_at,
35
+ requested_at, gate_id, requested_session, rejected_at, via, unattended },
36
+ created_at, updated_at, history[]
37
+ ```
38
+
39
+ `state.py` applies the schema-2 keys in memory as it loads, so a project that has
40
+ not run `/project-update` yet still answers every command; `save()` then writes
41
+ them. A reader that opens this file directly sees what is on disk, not those
42
+ defaults.
43
+
44
+ ## The other files beside it
45
+
46
+ | File | Written by | Committed |
47
+ |---|---|---|
48
+ | `current.json` | `state.py`, and `update.py` during a migration | no |
49
+ | `session.json` | `context-guard.py`, on SessionStart and each prompt | no |
50
+ | `handoff.md` | `state.py handoff` — never a model | no |
51
+ | `../reports/<task-id>/task.md` §Questions | `state.py ask/answer/questions` | yes |
52
+ | `../reports/<task-id>/events.jsonl` | `state.py` (`emit`), append-only | yes |
53
+
54
+ `handoff.md` is the ≤ 30-line frame a session that lost its context reads first:
55
+ the goal, the next action and its resume point, the pending questions, the latest
56
+ decisions, rejected options and failed attempts, and the user's last instruction
57
+ verbatim. It is rendered from the four sources above, so deleting it loses
58
+ nothing — the next stage-moving command writes it again.
59
+
60
+ `events.jsonl` is the journal: one line per state change, `history[]`'s facts
61
+ with types beside them. It is best effort by contract — a line that cannot be
62
+ written never fails the command that emitted it — so nothing depends on it being
63
+ complete, and readers skip a line they cannot parse.
64
+
65
+ If this file is corrupt, `state.py` refuses to read it rather than replacing it.
66
+ Inspect it by hand: the scope guard's boundaries come from here, and a silently
67
+ regenerated state file means an unbounded implementer.
@@ -0,0 +1,49 @@
1
+ {
2
+ "version": 1,
3
+ "axes": {
4
+ "scope": {"measure": "references", "bands": [0, 5], "hot_paths": ["hooks/**", "src/Shared/**"]},
5
+ "behaviour": {"measure": "session",
6
+ "keywords": ["workflow", "transition", "state machine", "messenger", "queue", "async", "transaction"]},
7
+ "reversibility": {"measure": "paths+content",
8
+ "needs_step": ["**/migrations/**", "**/Migrations/**", "config/**", "k8s/**", "**/env/**"],
9
+ "irreversible": ["**/prod/**", "**/production/**", "deploy/**", "**/*.deploy.*"],
10
+ "irreversible_content": ["DROP ", "TRUNCATE", "DELETE FROM", "ALTER .* DROP", "RENAME"]},
11
+ "domain": {"measure": "modules+session",
12
+ "modules": ["**/Payment/**", "**/Tax/**", "**/Invoice/**", "**/Security/**", "**/account/**"],
13
+ "customer_visible": ["templates/**", "**/Shop/**", "**/Resources/views/**"]},
14
+ "novelty": {"measure": "session",
15
+ "manifests": ["composer.json", "requirements*.txt", "package.json", "pyproject.toml", "Gemfile"]},
16
+ "coverage": {"measure": "tests-map",
17
+ "code_globs": ["**/*.php", "**/*.py", "**/*.js", "**/*.ts", "**/*.sh"],
18
+ "test_globs": ["tests/**", "spec/**", "**/*Test.php", "**/*.test.*", "**/*.spec.*", "**/test_*.py"],
19
+ "map": {}}
20
+ },
21
+ "hints": [
22
+ {"paths": ["**/login*", "**/Security/**", "**/*Auth*"], "axis": "domain", "note": "auth-shaped path"},
23
+ {"paths": ["skills/project-update/migrations/**"], "axis": "reversibility", "note": "touches user trees"},
24
+ {"paths": [".ai/policies/**", ".claude/**", ".codex/**", "CLAUDE.md", "AGENTS.md"], "axis": "behaviour", "note": "pipeline"},
25
+ {"paths": ["composer.json", "package.json", "requirements*.txt"], "axis": "novelty", "note": "dependency manifest"}
26
+ ],
27
+ "modes": {
28
+ "light": {"max_w": 2, "plan": "inline", "review": "on_red_fact", "reviewer": "BALANCED",
29
+ "human_stops": 0, "suite": "verify", "e2e": false},
30
+ "guarded": {"max_w": 7, "plan": "plan_mode", "review": "one", "reviewer": "BALANCED",
31
+ "human_stops": 1, "suite": "verify+e2e", "e2e": true},
32
+ "full": {"min_w": 8, "hard_axes": ["reversibility", "domain"], "plan": "written", "review": "one",
33
+ "reviewer": "STRONG", "security_focus_when": "domain=2", "human_stops": 2, "suite": "verify+e2e", "e2e": true},
34
+ "full_irreversible": {"when": "reversibility=2 and domain=2, or irreversible_content",
35
+ "human_stops": 3, "confirm_names_operation": true}
36
+ },
37
+ "rules": {"unknown_scores": 1, "hint_raises_by": 1, "hint_raises_once_per_axis": true,
38
+ "rescore_axes": ["reversibility", "domain"], "rescore_direction": "up", "lower_by": "human",
39
+ "steps_advice_over": 8},
40
+ "review_rounds_max": 2,
41
+ "remediation_rounds": 2,
42
+ "tests": {"max_suite_runs": 2, "timeout_seconds": 1800, "env_retries": 1, "log_max_bytes": 2097152},
43
+ "batch": {"default": false, "review_rounds_max": 3, "max_suite_runs": 8, "parallel": 3, "tier": "BALANCED"},
44
+ "examples": {
45
+ "light": ["a font or copy change in one template", "a test-only change", "a message-text change"],
46
+ "guarded": ["a regex fix in a hot guard script", "a dev-environment manifest", "a feature with a new screen and an additive migration"],
47
+ "full": ["a change in Payment/, Tax/ or Security/", "a migration that rewrites user trees", "a production manifest or secret"]
48
+ }
49
+ }
@@ -0,0 +1,32 @@
1
+ # Weight table
2
+
3
+ <!-- generated by `state.py weight --mirror` from weight.json; edit the JSON and regenerate. sha256: 5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 -->
4
+
5
+ Six axes, 0–2 each; W is their sum. Why each rule is what it is: the plugin's `docs/weight.md`.
6
+
7
+ | Axis | Measured by | Table |
8
+ |---|---|---|
9
+ | scope | references | bands: [0, 5]; hot_paths: ["hooks/**", "src/Shared/**"] |
10
+ | behaviour | session | keywords: ["workflow", "transition", "state machine", "messenger", "queue", "async", "transaction"] |
11
+ | reversibility | paths+content | needs_step: ["**/migrations/**", "**/Migrations/**", "config/**", "k8s/**", "**/env/**"]; irreversible: ["**/prod/**", "**/production/**", "deploy/**", "**/*.deploy.*"]; irreversible_content: ["DROP ", "TRUNCATE", "DELETE FROM", "ALTER .* DROP", "RENAME"] |
12
+ | domain | modules+session | modules: ["**/Payment/**", "**/Tax/**", "**/Invoice/**", "**/Security/**", "**/account/**"]; customer_visible: ["templates/**", "**/Shop/**", "**/Resources/views/**"] |
13
+ | novelty | session | manifests: ["composer.json", "requirements*.txt", "package.json", "pyproject.toml", "Gemfile"] |
14
+ | coverage | tests-map | code_globs: ["**/*.php", "**/*.py", "**/*.js", "**/*.ts", "**/*.sh"]; test_globs: ["tests/**", "spec/**", "**/*Test.php", "**/*.test.*", "**/*.spec.*", "**/test_*.py"]; map: {} |
15
+
16
+ | Mode | When | Plan | Review | Reviewer | Human stops | Suite |
17
+ |---|---|---|---|---|---|---|
18
+ | light | W ≤ 2 | inline | on_red_fact | BALANCED | 0 | verify |
19
+ | guarded | W ≤ 7 | plan_mode | one | BALANCED | 1 | verify+e2e |
20
+ | full | W ≥ 8 or reversibility or domain = 2 | written | one | STRONG | 2 | verify+e2e |
21
+ | full_irreversible | reversibility=2 and domain=2, or irreversible_content | as full | as full | as full | 3 | as full |
22
+
23
+ | Hint paths | Axis | Note |
24
+ |---|---|---|
25
+ | `**/login*`, `**/Security/**`, `**/*Auth*` | domain +1 | auth-shaped path |
26
+ | `skills/project-update/migrations/**` | reversibility +1 | touches user trees |
27
+ | `.ai/policies/**`, `.claude/**`, `.codex/**`, `CLAUDE.md`, `AGENTS.md` | behaviour +1 | pipeline |
28
+ | `composer.json`, `package.json`, `requirements*.txt` | novelty +1 | dependency manifest |
29
+
30
+ Rules: {"unknown_scores": 1, "hint_raises_by": 1, "hint_raises_once_per_axis": true, "rescore_axes": ["reversibility", "domain"], "rescore_direction": "up", "lower_by": "human", "steps_advice_over": 8}
31
+
32
+ Budgets: review_rounds_max 2 · remediation_rounds 2 · tests {"max_suite_runs": 2, "timeout_seconds": 1800, "env_retries": 1, "log_max_bytes": 2097152} · batch {"default": false, "review_rounds_max": 3, "max_suite_runs": 8, "parallel": 3, "tier": "BALANCED"}
@@ -0,0 +1,15 @@
1
+ # Constitution — {{PROJECT}}
2
+
3
+ <!-- The 10–15 principles this project does not negotiate, one line each, numbered C1…;
4
+ /sdlc-spec and the planner cite them. Under 15: what is not here lives in .ai/policies/. -->
5
+
6
+ C1. Production behaviour is the source of truth; what looks wrong may be load-bearing.
7
+ C2. A bugfix starts with the failing test; legacy behaviour gets a characterization test before it changes.
8
+ C3. A refactoring changes no behaviour and is never mixed with a feature.
9
+ C4. Every step names its files; scope grows only by amending the plan.
10
+ C5. No agent merges or deploys; an agent commits, pushes or approves only by running, verbatim and once, a command a human picked in the dialog; otherwise a human runs it outside the agent.
11
+ C6. <!-- project principle — filled by /ai-init from the survey, confirmed by you -->
12
+ C7. <!-- project principle -->
13
+ C8. <!-- project principle -->
14
+ C9. <!-- project principle -->
15
+ C10. <!-- project principle -->
@@ -0,0 +1,67 @@
1
+ # Task state
2
+
3
+ `state.json` is the machine-readable record of the task in flight. It is
4
+ written by `skills/ai-task/state.py` (atomically), read by `/ai-status`, and
5
+ enforced by `ai-scope-guard`, which derives the files the current step may touch
6
+ from it.
7
+
8
+ Two other writers exist, and only these two. A schema migration in
9
+ `/project-update` may change the state through `ctx.patch_state()`, with the
10
+ same atomic write and a `schema_migrated` history entry — that is how a task in
11
+ flight survives a change to the shape of this file. And `hooks/context-guard.py`
12
+ writes `session.json`, the sidecar beside this file, and nothing else: it never
13
+ touches `state.json`. Nothing else writes any of it — an agent never edits
14
+ them by hand, and the path guard refuses when one tries.
15
+
16
+ It is **not committed**: it describes a session, not the repository. The
17
+ `.gitignore` entry added by `/ai-init` keeps it out of git.
18
+
19
+ When a task closes, `state.py close` writes the final state to
20
+ `.ai/state/logs/<task-id>/state.json`, beside the task's verify logs — local,
21
+ **not** committed. What is committed is `.ai/reports/<task-id>/events.jsonl`
22
+ and `task.md`, and in packages mode the task's compact record under
23
+ `.ai/memory/` — that is the audit trail: which stages ran, in what order, who
24
+ approved what, and when (the plugin's `docs/local-memory.md`).
25
+
26
+ It holds facts, never transcripts:
27
+
28
+ ```
29
+ task_id, goal, workflow, risk_tier, current_stage, affected_modules,
30
+ context_summary_ref, approved_plan { ref, current_step_id, steps[] },
31
+ completed_steps, test_status, e2e_status, review_status, security_status, open_risks,
32
+ next_action, owner_runtime, resume_point { stage, step_id, next_action, at, runtime },
33
+ questions { file, pending[] }, handoff { file, written_at, reason },
34
+ human_approval { required, granted, granted_by, granted_at,
35
+ requested_at, gate_id, requested_session, rejected_at, via, unattended },
36
+ created_at, updated_at, history[]
37
+ ```
38
+
39
+ `state.py` applies the schema-2 keys in memory as it loads, so a project that has
40
+ not run `/project-update` yet still answers every command; `save()` then writes
41
+ them. A reader that opens this file directly sees what is on disk, not those
42
+ defaults.
43
+
44
+ ## The other files beside it
45
+
46
+ | File | Written by | Committed |
47
+ |---|---|---|
48
+ | `state.json` (legacy `current.json`, still read) | `state.py`, and `update.py` during a migration | no |
49
+ | `session.json` | `context-guard.py`, on SessionStart and each prompt | no |
50
+ | `handoff.md` | `state.py handoff` — never a model | no |
51
+ | `../reports/<task-id>/task.md` §Questions | `state.py ask/answer/questions` | yes |
52
+ | `../reports/<task-id>/events.jsonl` | `state.py` (`emit`), append-only | yes |
53
+
54
+ `handoff.md` is the ≤ 30-line frame a session that lost its context reads first:
55
+ the goal, the next action and its resume point, the pending questions, the latest
56
+ decisions, rejected options and failed attempts, and the user's last instruction
57
+ verbatim. It is rendered from the four sources above, so deleting it loses
58
+ nothing — the next stage-moving command writes it again.
59
+
60
+ `events.jsonl` is the journal: one line per state change, `history[]`'s facts
61
+ with types beside them. It is best effort by contract — a line that cannot be
62
+ written never fails the command that emitted it — so nothing depends on it being
63
+ complete, and readers skip a line they cannot parse.
64
+
65
+ If this file is corrupt, `state.py` refuses to read it rather than replacing it.
66
+ Inspect it by hand: the scope guard's boundaries come from here, and a silently
67
+ regenerated state file means an unbounded implementer.
@@ -0,0 +1,67 @@
1
+ # How agents work in this repository
2
+
3
+ One `.ai/` tree serves every runtime — same pipeline, same tiers, same scope rule
4
+ — and one task state under `state/`, so a task started in one runtime resumes in
5
+ the other. Read the non-negotiables below, then only the row for what you are doing.
6
+ Everything else loads on demand; nothing here is worth reading twice.
7
+
8
+ ## Non-negotiable (read even if you read nothing else)
9
+
10
+ 1. Production behaviour is the source of truth. Code that looks wrong may be
11
+ load-bearing: document what you find, change only what the task asked for.
12
+ 2. A change runs through `/rr-plan` and `/rr-build` (`using-riskrails.md`).
13
+ The task names the files it may touch; an edit outside them is refused —
14
+ answer `SCOPE_CHANGE_REQUIRED` and widen the task in place with
15
+ `state.py start --amend --files "+<path>" --why "…"`, never around the guard.
16
+ 3. No agent merges or deploys; commit, push, approval only as the dialog's picked
17
+ command, or the human runs them outside the agent.
18
+ 4. Verify before reporting done: the verification command once, to the end,
19
+ every failure fixed as one batch, the output shown. Run it through
20
+ `state.py verify --scope all` — it caps the runs, keeps the log out of the
21
+ context, and a green exit code needs no agent to interpret it.
22
+ 5. A refactoring changes no behaviour and is never mixed with a feature; legacy
23
+ behaviour without a test gets a characterization test first.
24
+ 6. Everything written into `project/` carries an evidence label — KNOWN FACT
25
+ with `file:line`, INFERENCE with what it came from, UNKNOWN, RISK. An
26
+ inference is never written as a fact.
27
+ 7. `docs/sdlc/constitution.md` is this project's own list of principles. Cite
28
+ them as `C<n>` in a spec, a plan or a review; say so in one line if it is
29
+ absent, and carry on.
30
+
31
+ ## Shared files
32
+
33
+ This `.ai/` holds `project/`, `rules/`, `state/`, `reports/`, `VERSION` and, in
34
+ `policies/`, `risk-tiers.json`, `risk-tiers.md`, `path-guard.json`, `testing.md`,
35
+ `coding.md`, `safety.md` and `security.md`. Every other path below ships with the
36
+ plugin at `<home>/riskrails/ai/<path>`; `<home>` is `~/.claude`, or
37
+ `$CODEX_HOME` (default `~/.codex`). A file at the same path here overrides it and
38
+ stops receiving its changes; `<home>/riskrails/ai-path <path>` prints the
39
+ copy that applies.
40
+
41
+ ## Doing X → read Y
42
+
43
+ | Doing | Read |
44
+ |---|---|
45
+ | anything, first | `policies/safety.md` |
46
+ | which command when, the three modes | `using-riskrails.md` |
47
+ | starting or resuming a task | `state.py handoff --print`, then `/rr-build` (no task: `/rr-plan`) |
48
+ | the weight, the mode, who reviews, what a human approves | `policies/weight.json` (`policies/weight.md` mirrors it) |
49
+ | choosing an agent or a tier | `policies/model-routing.md` |
50
+ | writing code under `<dir>/` | `policies/coding.md`; `rules/<slug>.md` for that directory |
51
+ | tests | `policies/testing.md` — Verification: the three commands, run by `state.py verify` |
52
+ | finishing a step, verifying, recording a review, closing | `state.py step-done`, `verify`, `review`, `close` — the measurements are deterministic and the refusals name the way out |
53
+ | schema, data, migrations | `policies/database.md`, `policies/production.md` |
54
+ | auth, secrets, personal data, payments | `policies/security.md` |
55
+ | git, release | `policies/git.md`, `policies/release.md` |
56
+ | feature / bugfix / refactoring / hotfix / investigation | `workflows.md`, the section of that name |
57
+ | acting as a role | `agents/<role>.md` |
58
+ | an artefact | `templates/<artefact>.md` → `reports/<task-id>/` |
59
+ | understanding the system | `project/overview.md`, then `project/*.md` |
60
+ | principles, intent, spec, plan, decisions | `docs/sdlc/constitution.md`, `docs/sdlc/` |
61
+ | the project is behind the plugin | `/project-update` (`VERSION` is this tree's schema) |
62
+
63
+ ## Keeping this directory true
64
+
65
+ `project/` describes the system as it was last surveyed. When you discover that it
66
+ is wrong, fix it in the same task — a knowledge base nobody trusts is worse than
67
+ none. Re-run `/ai-init` after a large change to refresh it.
@@ -0,0 +1,13 @@
1
+ <!-- riskrails:start -->
2
+ ## AI agent workflow
3
+
4
+ This repository runs an agentic pipeline under `.ai/`. Read `.ai/AGENTS.md` first: it routes to the policies, workflows and rules, which load on demand; `.ai/policies/` is binding.
5
+
6
+ - Production behaviour is the source of truth: document problems outside the task, do not fix them.
7
+
8
+ - Changes run through the pipeline (`/rr-build` in Claude Code or Codex); without it, follow `.ai/policies/safety.md`, touch only the files the current task names, and answer questions in prose or by filling the file.
9
+
10
+ - Verify before reporting done: `verify_command` from `.ai/policies/testing.md` once, to the end, then `e2e_command` once; every failure fixed as one batch; show the output.
11
+ - No agent merges or deploys; commit, push, approval only as the dialog picks, else a human outside. When a review flags the same mistake twice, the correction goes into this file.
12
+ - Files, not chat, carry decisions: `.ai/reports/<task-id>/task.md` (answer by filling `[Answer]:`), `.ai/state/handoff.md` (read first when resuming), `docs/sdlc/constitution.md` (this project's principles).
13
+ <!-- riskrails:end -->
@@ -0,0 +1,84 @@
1
+ # Safety policy
2
+
3
+ This is the file every agent reads first. It is short on purpose. It binds every
4
+ agent in every runtime — Claude Code and Codex alike.
5
+
6
+ ## The one rule
7
+
8
+ **Production behaviour is the source of truth until proven otherwise.** Code that
9
+ looks wrong, dead, or badly written may be load-bearing. The repository's history
10
+ is evidence; your intuition about how the code "should" look is not.
11
+
12
+ ## What an agent must never do
13
+
14
+ - Change application code that the current task did not ask about.
15
+ - Rename, move, reorganise or "clean up" anything as a side effect.
16
+ - Remove code because it looks unused. Find the callers first; if there are none,
17
+ say so in the report and leave the code alone.
18
+ - Combine a refactoring with a feature change in the same step.
19
+ - Modify database schemas, dependencies or framework versions outside a task that
20
+ is explicitly about that.
21
+ - Weaken a test, a linter rule or a static-analysis level to make a check pass.
22
+ - Commit, push, merge or deploy on its own initiative.
23
+ - Read production secrets or customer data into the model context.
24
+
25
+ If you find a problem outside the current task: **write it down, do not fix it.**
26
+ Unrelated findings are recorded with `state.py memory risk add` (in files mode it
27
+ writes `.ai/project/known-risks.md`) or go into the report's "observations"
28
+ section, and the human decides whether they become their own task.
29
+
30
+ The compact record is the human's to switch: `state.py memory enable`, `disable`,
31
+ `cleanup`, `pack`, `redact` and `migrate --move-logs` take the human route, as
32
+ `approve` does, and `.ai/memory/` and `.ai/local/memory/` are written by
33
+ `state.py` alone — an agent never edits them (`state.py memory --help`; the
34
+ plugin's `docs/local-memory.md`).
35
+
36
+ ## Scope
37
+
38
+ Every implementation step names the files it may touch. Editing anything else is
39
+ refused by `ai-scope-guard` and must be answered with `SCOPE_CHANGE_REQUIRED`:
40
+ stop, say which file you need and why, and let the plan be amended. A step whose
41
+ scope keeps growing is a step that was planned wrong.
42
+
43
+ `ai-git-guard`, `ai-path-guard` and `ai-scope-guard` all run in both runtimes. A
44
+ multi-file edit in one call — Codex's `apply_patch` — is checked path by path, so
45
+ a single out-of-scope or protected file rejects the whole patch. What a guard
46
+ refuses is not a hint: do not reword the command to get past it.
47
+
48
+ A guard is a backstop, not the policy. Some checks exist in only one runtime —
49
+ Codex has no hookable read tool, so the large-read cap is Claude-only there — and
50
+ an agent that behaves correctly only because a hook is watching is not behaving
51
+ correctly. The rules above hold whether or not a hook enforces them.
52
+
53
+ **The guards' threat model.** The guards defend against an agent's mistakes — a
54
+ careless `rm`, a write to a frozen policy during a task, a commit with a
55
+ secret — not against an agent that constructs a command to exhaust them. A
56
+ command built to time the guard out is visible in the transcript and in the
57
+ journal, and the human gate in full and full_irreversible mode is the boundary
58
+ for anything that matters.
59
+ Approval rests on a guard plus a ledger: a command the agent constructs to pass
60
+ for the human's pick is outside the threat model, and `state.py consent audit`
61
+ detects it afterwards.
62
+
63
+ ## Evidence labels
64
+
65
+ Anything an agent writes into `.ai/project/` carries one of four labels:
66
+
67
+ - **KNOWN FACT** — read from the code, a config file, a migration or a test.
68
+ Cite `file:line`.
69
+ - **INFERENCE** — a conclusion drawn from facts. Say what it is drawn from.
70
+ - **UNKNOWN** — something that matters and could not be determined. Say what
71
+ would answer it.
72
+ - **RISK** — something that looks dangerous. Say what could go wrong.
73
+
74
+ Never present an inference as a fact. When documentation and code disagree,
75
+ record both under "Documented behaviour" and "Observed behaviour", with the
76
+ evidence for each, and let the human decide which is the bug.
77
+
78
+ ## When in doubt
79
+
80
+ Stop and ask. An agent that pauses costs a message; an agent that guesses on a
81
+ payment path costs an incident.
82
+
83
+ Doubt on reversibility or domain is a blocker; other doubt is written to
84
+ `task.md` and the work continues.
@@ -0,0 +1,13 @@
1
+ <!-- riskrails:start -->
2
+ ## AI agent workflow
3
+
4
+ This repository runs an agentic pipeline under `.ai/`. Read `.ai/AGENTS.md` first: it routes to the policies, workflows and rules, which load on demand; `.ai/policies/` is binding.
5
+
6
+ - Production behaviour is the source of truth: document problems outside the task, do not fix them.
7
+
8
+ - A change runs through `/rr-build <request>`; `/rr-status` shows where it stands. Each step names the files it may touch — an edit outside them is refused: answer `SCOPE_CHANGE_REQUIRED`. One `apply_patch` is checked file by file: a patch that reaches outside the step is refused whole.
9
+
10
+ - Verify before reporting done: `verify_command` from `.ai/policies/testing.md` once, to the end, then `e2e_command` once; every failure fixed as one batch; show the output.
11
+ - No agent merges or deploys; commit, push, approval only as the dialog picks, else a human outside. When a review flags the same mistake twice, the correction goes into this file.
12
+ - Files, not chat, carry decisions: `.ai/reports/<task-id>/task.md` (answer by filling `[Answer]:`), `.ai/state/handoff.md` (read first when resuming), `docs/sdlc/constitution.md` (this project's principles).
13
+ <!-- riskrails:end -->
@@ -0,0 +1,74 @@
1
+ # Safety policy
2
+
3
+ This is the file every agent reads first. It is short on purpose. It binds every
4
+ agent in every runtime — Claude Code and Codex alike.
5
+
6
+ ## The one rule
7
+
8
+ **Production behaviour is the source of truth until proven otherwise.** Code that
9
+ looks wrong, dead, or badly written may be load-bearing. The repository's history
10
+ is evidence; your intuition about how the code "should" look is not.
11
+
12
+ ## What an agent must never do
13
+
14
+ - Change application code that the current task did not ask about.
15
+ - Rename, move, reorganise or "clean up" anything as a side effect.
16
+ - Remove code because it looks unused. Find the callers first; if there are none,
17
+ say so in the report and leave the code alone.
18
+ - Combine a refactoring with a feature change in the same step.
19
+ - Modify database schemas, dependencies or framework versions outside a task that
20
+ is explicitly about that.
21
+ - Weaken a test, a linter rule or a static-analysis level to make a check pass.
22
+ - Commit, push, merge or deploy on its own initiative.
23
+ - Read production secrets or customer data into the model context.
24
+
25
+ If you find a problem outside the current task: **write it down, do not fix it.**
26
+ Unrelated findings are recorded with `state.py memory risk add` (in files mode it
27
+ writes `.ai/project/known-risks.md`) or go into the report's "observations"
28
+ section, and the human decides whether they become their own task.
29
+
30
+ The compact record is the human's to switch: `state.py memory enable`, `disable`,
31
+ `cleanup`, `pack`, `redact` and `migrate --move-logs` take the human route, as
32
+ `approve` does, and `.ai/memory/` and `.ai/local/memory/` are written by
33
+ `state.py` alone — an agent never edits them (`state.py memory --help`; the
34
+ plugin's `docs/local-memory.md`).
35
+
36
+ ## Scope
37
+
38
+ Every implementation step names the files it may touch. Editing anything else is
39
+ refused by `ai-scope-guard` and must be answered with `SCOPE_CHANGE_REQUIRED`:
40
+ stop, say which file you need and why, and let the plan be amended. A step whose
41
+ scope keeps growing is a step that was planned wrong.
42
+
43
+ `ai-git-guard`, `ai-path-guard` and `ai-scope-guard` all run in both runtimes. A
44
+ multi-file edit in one call — Codex's `apply_patch` — is checked path by path, so
45
+ a single out-of-scope or protected file rejects the whole patch. What a guard
46
+ refuses is not a hint: do not reword the command to get past it.
47
+
48
+ A guard is a backstop, not the policy. Some checks exist in only one runtime —
49
+ Codex has no hookable read tool, so the large-read cap is Claude-only there — and
50
+ an agent that behaves correctly only because a hook is watching is not behaving
51
+ correctly. The rules above hold whether or not a hook enforces them.
52
+
53
+ ## Evidence labels
54
+
55
+ Anything an agent writes into `.ai/project/` carries one of four labels:
56
+
57
+ - **KNOWN FACT** — read from the code, a config file, a migration or a test.
58
+ Cite `file:line`.
59
+ - **INFERENCE** — a conclusion drawn from facts. Say what it is drawn from.
60
+ - **UNKNOWN** — something that matters and could not be determined. Say what
61
+ would answer it.
62
+ - **RISK** — something that looks dangerous. Say what could go wrong.
63
+
64
+ Never present an inference as a fact. When documentation and code disagree,
65
+ record both under "Documented behaviour" and "Observed behaviour", with the
66
+ evidence for each, and let the human decide which is the bug.
67
+
68
+ ## When in doubt
69
+
70
+ Stop and ask. An agent that pauses costs a message; an agent that guesses on a
71
+ payment path costs an incident.
72
+
73
+ Doubt on reversibility or domain is a blocker; other doubt is written to
74
+ `task.md` and the work continues.
@@ -0,0 +1,71 @@
1
+ # Safety policy
2
+
3
+ This is the file every agent reads first. It is short on purpose. It binds every
4
+ agent in every runtime — Claude Code and Codex alike.
5
+
6
+ ## The one rule
7
+
8
+ **Production behaviour is the source of truth until proven otherwise.** Code that
9
+ looks wrong, dead, or badly written may be load-bearing. The repository's history
10
+ is evidence; your intuition about how the code "should" look is not.
11
+
12
+ ## What an agent must never do
13
+
14
+ - Change application code that the current task did not ask about.
15
+ - Rename, move, reorganise or "clean up" anything as a side effect.
16
+ - Remove code because it looks unused. Find the callers first; if there are none,
17
+ say so in the report and leave the code alone.
18
+ - Combine a refactoring with a feature change in the same step.
19
+ - Modify database schemas, dependencies or framework versions outside a task that
20
+ is explicitly about that.
21
+ - Weaken a test, a linter rule or a static-analysis level to make a check pass.
22
+ - Commit, push, merge or deploy on its own initiative.
23
+ - Read production secrets or customer data into the model context.
24
+
25
+ If you find a problem outside the current task: **write it down, do not fix it.**
26
+ Unrelated findings are recorded with `state.py memory risk add` (in files mode it
27
+ writes `.ai/project/known-risks.md`) or go into the report's "observations"
28
+ section, and the human decides whether they become their own task.
29
+
30
+ The compact record is the human's to switch: `state.py memory enable`, `disable`,
31
+ `cleanup`, `pack`, `redact` and `migrate --move-logs` take the human route, as
32
+ `approve` does, and `.ai/memory/` and `.ai/local/memory/` are written by
33
+ `state.py` alone — an agent never edits them (`state.py memory --help`; the
34
+ plugin's `docs/local-memory.md`).
35
+
36
+ ## Scope
37
+
38
+ Every implementation step names the files it may touch. Editing anything else is
39
+ refused by `ai-scope-guard` and must be answered with `SCOPE_CHANGE_REQUIRED`:
40
+ stop, say which file you need and why, and let the plan be amended. A step whose
41
+ scope keeps growing is a step that was planned wrong.
42
+
43
+ `ai-git-guard`, `ai-path-guard` and `ai-scope-guard` all run in both runtimes. A
44
+ multi-file edit in one call — Codex's `apply_patch` — is checked path by path, so
45
+ a single out-of-scope or protected file rejects the whole patch. What a guard
46
+ refuses is not a hint: do not reword the command to get past it.
47
+
48
+ A guard is a backstop, not the policy. Some checks exist in only one runtime —
49
+ Codex has no hookable read tool, so the large-read cap is Claude-only there — and
50
+ an agent that behaves correctly only because a hook is watching is not behaving
51
+ correctly. The rules above hold whether or not a hook enforces them.
52
+
53
+ ## Evidence labels
54
+
55
+ Anything an agent writes into `.ai/project/` carries one of four labels:
56
+
57
+ - **KNOWN FACT** — read from the code, a config file, a migration or a test.
58
+ Cite `file:line`.
59
+ - **INFERENCE** — a conclusion drawn from facts. Say what it is drawn from.
60
+ - **UNKNOWN** — something that matters and could not be determined. Say what
61
+ would answer it.
62
+ - **RISK** — something that looks dangerous. Say what could go wrong.
63
+
64
+ Never present an inference as a fact. When documentation and code disagree,
65
+ record both under "Documented behaviour" and "Observed behaviour", with the
66
+ evidence for each, and let the human decide which is the bug.
67
+
68
+ ## When in doubt
69
+
70
+ Stop and ask. An agent that pauses costs a message; an agent that guesses on a
71
+ payment path costs an incident.