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.
Files changed (99) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/README.md +29 -15
  5. package/ai/policies/model-routing.md +7 -7
  6. package/ai/policies/production.md +3 -3
  7. package/ai/policies/release.md +2 -2
  8. package/ai/policies/review-economy.md +27 -30
  9. package/bin/riskrails.js +114 -23
  10. package/hooks/ai-path-guard-defaults.json +3 -2
  11. package/hooks/context-guard.py +15 -12
  12. package/hooks/dialog-gate.py +1 -1
  13. package/hooks/guard/common.py +2 -0
  14. package/hooks/guard/dialog.py +107 -27
  15. package/hooks/guard/git.py +12 -3
  16. package/hooks/guard/paths.py +150 -37
  17. package/hooks/project-scaffold.py +3 -6
  18. package/hooks/runtime-gate.py +134 -33
  19. package/hooks/update_notice.py +22 -16
  20. package/instructions/routing.md +1 -1
  21. package/instructions/stub.md +3 -3
  22. package/package.json +1 -1
  23. package/skills/AGENTS.md +1 -1
  24. package/skills/CLAUDE.md +1 -1
  25. package/skills/ai-init/templates/.ai/AGENTS.md +1 -1
  26. package/skills/ai-init/templates/.ai/VERSION +1 -1
  27. package/skills/ai-init/templates/.ai/policies/security.md +7 -1
  28. package/skills/ai-init/templates/.ai/policies/testing.md +19 -6
  29. package/skills/ai-init/templates/.ai/state/README.md +4 -4
  30. package/skills/ai-init/templates/AGENTS.block.md +2 -2
  31. package/skills/ai-init/templates/CLAUDE.block.md +2 -2
  32. package/skills/ai-init/templates/GEMINI.block.md +2 -2
  33. package/skills/ai-init/templates/gitignore.snippet +1 -0
  34. package/skills/ai-init/templates/junie-guidelines.block.md +2 -2
  35. package/skills/ai-task/rr_state/__init__.py +1 -0
  36. package/skills/ai-task/rr_state/cli.py +210 -0
  37. package/skills/ai-task/rr_state/close.py +171 -0
  38. package/skills/ai-task/rr_state/dialog.py +541 -0
  39. package/skills/ai-task/rr_state/dialog_cmd.py +164 -0
  40. package/skills/ai-task/rr_state/fs.py +220 -0
  41. package/skills/ai-task/rr_state/gate.py +125 -0
  42. package/skills/ai-task/rr_state/gate_cmd.py +128 -0
  43. package/skills/ai-task/rr_state/handoff.py +226 -0
  44. package/skills/ai-task/rr_state/journal.py +126 -0
  45. package/skills/ai-task/rr_state/labels.py +99 -0
  46. package/skills/ai-task/rr_state/ledger.py +145 -0
  47. package/skills/ai-task/rr_state/locks.py +84 -0
  48. package/skills/ai-task/rr_state/memory_cmd.py +310 -0
  49. package/skills/ai-task/rr_state/policy.py +378 -0
  50. package/skills/ai-task/rr_state/profile.py +373 -0
  51. package/skills/ai-task/rr_state/questions.py +336 -0
  52. package/skills/ai-task/rr_state/questions_cmd.py +321 -0
  53. package/skills/ai-task/rr_state/report.py +271 -0
  54. package/skills/ai-task/rr_state/report_cmd.py +230 -0
  55. package/skills/ai-task/rr_state/review.py +165 -0
  56. package/skills/ai-task/rr_state/runtime.py +80 -0
  57. package/skills/ai-task/rr_state/start.py +257 -0
  58. package/skills/ai-task/rr_state/steps.py +131 -0
  59. package/skills/ai-task/rr_state/task.py +396 -0
  60. package/skills/ai-task/rr_state/verify.py +231 -0
  61. package/skills/ai-task/sensors.py +37 -4
  62. package/skills/ai-task/state.py +24 -5138
  63. package/skills/project-init/templates/constitution.md +1 -1
  64. package/skills/project-init/templates/sdlc-README.md +20 -14
  65. package/skills/project-update/adopt.py +38 -24
  66. package/skills/project-update/history/blobs/00289f98434fed230eebe768dfb3425f83d737866a3aebc97aa95167c4fa0c6e +131 -0
  67. package/skills/project-update/history/blobs/079a91f976ad69b5cf49ccb9a28849576e4735ddd9485fc26599e528a69fd2f4 +30 -0
  68. package/skills/project-update/history/blobs/07ff7fa6f93d471ff533f553b4e523e4105ccde1f3dab70132c5237728f68422 +13 -0
  69. package/skills/project-update/history/blobs/0ef4a360c76da290173dea0f8073949cb0f8113710a38df95acae5d96bdd1286 +123 -0
  70. package/skills/project-update/history/blobs/18c7a2753ce9cb6f6d3d1e9d5d1125d75950b879d56b7325225b200869631b29 +15 -0
  71. package/skills/project-update/history/blobs/25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e +1 -0
  72. package/skills/project-update/history/blobs/48d9950f16e31d55475f566a80dc95f64b35f1c804d2f51729e313a4f29bac8d +13 -0
  73. package/skills/project-update/history/blobs/5a93f78b4ed4d416c171139b2ce610a117f9c1dd46fbb56ca86729cb4c53d10c +6 -0
  74. package/skills/project-update/history/blobs/6a3be0e624e5ad91e44d08857b7183ee53b7903e1e8d56ff196dbc9dde271e4a +75 -0
  75. package/skills/project-update/history/blobs/7170553410bba904ffb7edb4b231e3b3579b85542d84de717299980938f82c9d +13 -0
  76. package/skills/project-update/history/blobs/774b1e3a04ef0d49d33c44a2cfecd92eb6f6b738a12b6a681103ba7fc13e7b86 +67 -0
  77. package/skills/project-update/history/blobs/7f063e79195a5c85c37b7260dd20c184d3b9bea85e37202e0bbca73ad84bba49 +13 -0
  78. package/skills/project-update/history/blobs/a51517e13e57420d2d88a6bcfb4055d762d4a0d10a8f88450929c5c4572213a9 +67 -0
  79. package/skills/project-update/history/blobs/c8573ca1f810ce6c4ac49456a4c1725b93e0d3b74c54ce95d46d8747777895f0 +13 -0
  80. package/skills/project-update/history/blobs/d6e5b34d475e7187d2a8cb2aac7e7f1a427142baf6461bda387d70ceb4d8a8c1 +30 -0
  81. package/skills/project-update/history/blobs/e120ff4e32b2e950fc94ada4e787d57dc9c790da37223e5a202d74f7e3aadf80 +13 -0
  82. package/skills/project-update/history/index.json +38 -16
  83. package/skills/project-update/migrations/0003_context_diet.py +1 -1
  84. package/skills/project-update/migrations/0007_weight_model.py +130 -23
  85. package/skills/project-update/migrations/0011_retire_risk_tiers.py +37 -0
  86. package/skills/project-update/migrations/README.md +16 -1
  87. package/skills/project-update/migrations/__init__.py +6 -1
  88. package/skills/project-update/render_instructions.py +1 -1
  89. package/skills/project-update/update.py +569 -106
  90. package/skills/rr-build-all/SKILL.md +2 -2
  91. package/skills/rr-plan/SKILL.md +3 -1
  92. package/skills/rr-review/SKILL.md +13 -8
  93. package/skills/rr-setup/SKILL.md +24 -4
  94. package/skills/rr-setup/setup.py +7 -1
  95. package/skills/rr-spec/SKILL.md +4 -2
  96. package/skills/rr-status/status.py +36 -9
  97. package/skills/usage-report/usage-report.py +4 -2
  98. package/skills/ai-init/templates/.ai/policies/risk-tiers.json +0 -628
  99. package/skills/ai-init/templates/.ai/policies/risk-tiers.md +0 -180
@@ -135,8 +135,10 @@ def snapshot_tree(root):
135
135
  def numstat(root, from_tree, to_tree):
136
136
  """[{path, added, deleted, binary}] between two trees, NUL-separated so a
137
137
  path with a space, a quote or a newline survives. Renames are off: a rename
138
- is an add plus a delete, as diff_files reports it."""
139
- code, out = git(root, ["diff", "--numstat", "--no-renames", "-z",
138
+ is an add plus a delete, as diff_files reports it. --relative (git runs in
139
+ root): paths are relative to the project root, as task files and policy globs
140
+ are, and a change outside root, below the git top level, is left out."""
141
+ code, out = git(root, ["diff", "--relative", "--numstat", "--no-renames", "-z",
140
142
  from_tree, to_tree])
141
143
  if code != 0:
142
144
  return None
@@ -170,7 +172,8 @@ def diff_files(root, from_tree, to_tree):
170
172
  None when either tree is missing: an unmeasured diff is not an empty one."""
171
173
  if not from_tree or not to_tree:
172
174
  return None
173
- code, out = git(root, ["diff", "--name-status", "--no-renames", "-z", from_tree, to_tree])
175
+ code, out = git(root, ["diff", "--relative", "--name-status", "--no-renames", "-z",
176
+ from_tree, to_tree])
174
177
  if code != 0:
175
178
  return None
176
179
  fields = [f for f in out.split("\0") if f != ""]
@@ -202,7 +205,8 @@ def scope_any(path, patterns):
202
205
  # ------------------------------------------------------------------- running
203
206
 
204
207
  TESTING_FIELDS = ("verify_command", "step_test_command", "e2e_command",
205
- "lint_command", "typecheck_command", "single_test", "doc_tests")
208
+ "lint_command", "typecheck_command", "single_test", "doc_tests",
209
+ "bite_codes")
206
210
 
207
211
  SCOPE_FIELD = {"suite": "verify_command", "step": "step_test_command",
208
212
  "e2e": "e2e_command", "single": "single_test"}
@@ -232,10 +236,39 @@ def read_commands(root):
232
236
  value = match.group(1).strip()
233
237
  if value.startswith("#"):
234
238
  value = ""
239
+ # A trailing ` # comment` is not part of `none` or of the code list; a
240
+ # command value is left exactly as written (a `#` can be part of it).
241
+ bare = re.sub(r"\s+#.*$", "", value)
242
+ if field == "bite_codes" or bare.lower() == "none":
243
+ value = bare
235
244
  commands[field] = value
236
245
  return commands
237
246
 
238
247
 
248
+ def _escaped(text):
249
+ """A value from a file, made safe to print: a control character or an ESC
250
+ sequence is shown as its escape, never sent to the terminal."""
251
+ return "".join(c if c.isprintable() else repr(c)[1:-1] for c in text)
252
+
253
+
254
+ def bite_codes(root):
255
+ """The exit codes that count as "the test fails" under --expect fail: the
256
+ testing.md `bite_codes` list of integers 1-255, 1 when absent or not a clean list
257
+ (an empty part, a non-integer, 0, a negative or a value over 255 all fall back)."""
258
+ raw = read_commands(root).get("bite_codes", "")
259
+ try:
260
+ codes = tuple(int(part) for part in raw.split(","))
261
+ usable = all(1 <= code <= 255 for code in codes)
262
+ except ValueError:
263
+ usable = False
264
+ if usable:
265
+ return codes
266
+ if raw: # present but unusable: say so (stderr keeps stdout's contract)
267
+ print('sensors: bite_codes "%s" is not a list of 1-255 exit codes - using 1' % _escaped(raw),
268
+ file=sys.stderr)
269
+ return (1,)
270
+
271
+
239
272
  def command_for(root, scope):
240
273
  return (read_commands(root).get(SCOPE_FIELD.get(scope, ""), "") or "").strip()
241
274