riskrails 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/README.md +246 -812
- package/agents/ai-reviewer.md.tmpl +6 -4
- package/ai/policies/review-economy.md +7 -2
- package/ai/using-riskrails.md +15 -0
- package/bin/claude-1m +15 -14
- package/bin/riskrails.js +108 -32
- package/codex/hooks.json +25 -2
- package/hooks/AGENTS.md +2 -2
- package/hooks/CLAUDE.md +2 -2
- package/hooks/ai-guard.py +29 -8
- package/hooks/ai-path-guard-defaults.json +3 -1
- package/hooks/codex-hooks.json +25 -2
- package/hooks/context-guard.py +251 -35
- package/hooks/dialog-gate.py +105 -0
- package/hooks/guard/common.py +5 -0
- package/hooks/guard/decision.py +123 -0
- package/hooks/guard/dialog.py +598 -0
- package/hooks/guard/git.py +49 -3
- package/hooks/guard/paths.py +62 -3
- package/hooks/hooks.json +37 -2
- package/hooks/memory-pre-push.py +28 -0
- package/hooks/runtime-gate.py +59 -12
- package/hooks/update_notice.py +135 -0
- package/instructions/routing.md +4 -4
- package/instructions/stub.md +2 -2
- package/package.json +1 -1
- package/profiles/max.json +3 -1
- package/profiles/pro.json +3 -1
- package/scripts/merge-codex-config.py +134 -0
- package/scripts/resolve-profile.py +15 -3
- package/settings.common.json +39 -2
- package/skills/AGENTS.md +2 -2
- package/skills/CLAUDE.md +2 -2
- package/skills/ai-init/templates/.ai/AGENTS.md +2 -2
- package/skills/ai-init/templates/.ai/VERSION +1 -1
- package/skills/ai-init/templates/.ai/policies/dialog.json +1 -0
- package/skills/ai-init/templates/.ai/policies/safety.md +22 -2
- package/skills/ai-init/templates/.ai/policies/weight.json +1 -0
- package/skills/ai-init/templates/.ai/policies/weight.md +2 -2
- package/skills/ai-init/templates/.ai/state/README.md +9 -6
- package/skills/ai-init/templates/AGENTS.block.md +1 -1
- package/skills/ai-init/templates/CLAUDE.block.md +1 -1
- package/skills/ai-init/templates/GEMINI.block.md +1 -1
- package/skills/ai-init/templates/junie-guidelines.block.md +1 -1
- package/skills/ai-task/memory_compact.py +968 -0
- package/skills/ai-task/memory_format.py +775 -0
- package/skills/ai-task/memory_store.py +528 -0
- package/skills/ai-task/memory_sync.py +1571 -0
- package/skills/ai-task/sensors.py +2 -2
- package/skills/ai-task/state.py +967 -70
- package/skills/ai-task/weight.py +3 -3
- package/skills/project-init/templates/constitution.md +1 -1
- package/skills/project-update/history/blobs/164cd34c182de88c33de7df35beb29200d792419e1c13a5f0b2104f944e57be7 +1 -0
- package/skills/project-update/history/blobs/1d1d907449ec74594d0ee923ac142ae746b9aedf0a1eda9469f075a44d23e3d7 +13 -0
- package/skills/project-update/history/blobs/23a42febbacaa7b52a802d428033a99c0694391c81d51fe3b6e1dff608a641d5 +67 -0
- package/skills/project-update/history/blobs/5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 +49 -0
- package/skills/project-update/history/blobs/5f65bc7ab3fc3c4e75ae05d49eaf4331fafc398c661abe33c69f7380a720344c +32 -0
- package/skills/project-update/history/blobs/7be527cd648b27cca6e267de30f8e33a2ce17d5631a82694f23ae5c0623efe1f +15 -0
- package/skills/project-update/history/blobs/7ee99edafe7cec45ff577376c0d0178fdb2690134d4656488f46e129aba3cc5a +67 -0
- package/skills/project-update/history/blobs/8b3ef9586491a426767c01da5d01bdd6b0655c576c68c4cc6dfb093367817dc0 +67 -0
- package/skills/project-update/history/blobs/917df3320d778ddbaa5c5c7742bc4046bf803c36ed2b050f30844ed206783469 +1 -0
- package/skills/project-update/history/blobs/935af1019931058ec3a0b834fe230e127ec914f75edcc4a6c589cfe733b0b979 +13 -0
- package/skills/project-update/history/blobs/ae5ce131d6bae401199ff32d8c3dc4d8aec188010a9111624ac64506e2116ff4 +84 -0
- package/skills/project-update/history/blobs/ca4499d829268c8766fb3b825a515e88dc55c237e9d202816b50959b4dac031f +13 -0
- package/skills/project-update/history/blobs/e9adb7a9905eb719ac78ab96671f055da53e5001810f5ad6d6c94306b0323fe7 +74 -0
- package/skills/project-update/history/blobs/fae3064e648cdc55be4aeb1177525b302a5e34ebd752e4a904f47dc0f4557b96 +71 -0
- package/skills/project-update/history/index.json +32 -13
- package/skills/project-update/migrations/0009_state_file.py +25 -0
- package/skills/project-update/migrations/0010_dialog_switch.py +30 -0
- package/skills/project-update/migrations/README.md +2 -1
- package/skills/project-update/update.py +12 -3
- package/skills/rr-build/SKILL.md +14 -5
- package/skills/rr-build-all/SKILL.md +190 -0
- package/skills/rr-plan/SKILL.md +13 -3
- package/skills/rr-review/SKILL.md +18 -8
- package/skills/rr-ship/SKILL.md +16 -8
- package/skills/rr-status/SKILL.md +2 -2
- package/skills/usage-report/usage-report.py +41 -8
package/skills/ai-task/weight.py
CHANGED
|
@@ -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("
|
|
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,
|
|
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 @@
|
|
|
1
|
+
10
|
|
@@ -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.
|