riskrails 3.0.0 → 3.1.0
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 +259 -812
- package/agents/ai-reviewer.md.tmpl +6 -4
- package/ai/policies/model-routing.md +7 -7
- package/ai/policies/production.md +3 -3
- package/ai/policies/release.md +2 -2
- package/ai/policies/review-economy.md +34 -32
- package/ai/using-riskrails.md +15 -0
- package/bin/claude-1m +15 -14
- package/bin/riskrails.js +113 -37
- 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 +252 -36
- package/hooks/dialog-gate.py +105 -0
- package/hooks/guard/common.py +7 -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 +89 -19
- package/hooks/hooks.json +37 -2
- package/hooks/memory-pre-push.py +28 -0
- package/hooks/project-scaffold.py +1 -5
- package/hooks/runtime-gate.py +65 -18
- package/hooks/update_notice.py +141 -0
- package/instructions/routing.md +4 -4
- package/instructions/stub.md +3 -3
- 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 +3 -3
- package/skills/CLAUDE.md +3 -3
- package/skills/ai-init/templates/.ai/AGENTS.md +3 -3
- 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/security.md +7 -1
- package/skills/ai-init/templates/.ai/policies/testing.md +19 -6
- 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 +2 -2
- package/skills/ai-init/templates/CLAUDE.block.md +2 -2
- package/skills/ai-init/templates/GEMINI.block.md +2 -2
- package/skills/ai-init/templates/gitignore.snippet +1 -0
- package/skills/ai-init/templates/junie-guidelines.block.md +2 -2
- package/skills/ai-task/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 +25 -5
- package/skills/ai-task/state.py +999 -79
- package/skills/ai-task/weight.py +3 -3
- package/skills/project-init/templates/constitution.md +1 -1
- package/skills/project-init/templates/sdlc-README.md +20 -14
- package/skills/project-update/history/blobs/00289f98434fed230eebe768dfb3425f83d737866a3aebc97aa95167c4fa0c6e +131 -0
- package/skills/project-update/history/blobs/079a91f976ad69b5cf49ccb9a28849576e4735ddd9485fc26599e528a69fd2f4 +30 -0
- package/skills/project-update/history/blobs/07ff7fa6f93d471ff533f553b4e523e4105ccde1f3dab70132c5237728f68422 +13 -0
- package/skills/project-update/history/blobs/0ef4a360c76da290173dea0f8073949cb0f8113710a38df95acae5d96bdd1286 +123 -0
- package/skills/project-update/history/blobs/164cd34c182de88c33de7df35beb29200d792419e1c13a5f0b2104f944e57be7 +1 -0
- package/skills/project-update/history/blobs/18c7a2753ce9cb6f6d3d1e9d5d1125d75950b879d56b7325225b200869631b29 +15 -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/25d4f2a86deb5e2574bb3210b67bb24fcc4afb19f93a7b65a057daa874a9d18e +1 -0
- package/skills/project-update/history/blobs/48d9950f16e31d55475f566a80dc95f64b35f1c804d2f51729e313a4f29bac8d +13 -0
- package/skills/project-update/history/blobs/5353ca529c46f2443e12947ec205a6887fb594a4de1f87f0eb1c0e6c0f826133 +49 -0
- package/skills/project-update/history/blobs/5a93f78b4ed4d416c171139b2ce610a117f9c1dd46fbb56ca86729cb4c53d10c +6 -0
- package/skills/project-update/history/blobs/5f65bc7ab3fc3c4e75ae05d49eaf4331fafc398c661abe33c69f7380a720344c +32 -0
- package/skills/project-update/history/blobs/6a3be0e624e5ad91e44d08857b7183ee53b7903e1e8d56ff196dbc9dde271e4a +75 -0
- package/skills/project-update/history/blobs/7170553410bba904ffb7edb4b231e3b3579b85542d84de717299980938f82c9d +13 -0
- package/skills/project-update/history/blobs/774b1e3a04ef0d49d33c44a2cfecd92eb6f6b738a12b6a681103ba7fc13e7b86 +67 -0
- package/skills/project-update/history/blobs/7be527cd648b27cca6e267de30f8e33a2ce17d5631a82694f23ae5c0623efe1f +15 -0
- package/skills/project-update/history/blobs/7ee99edafe7cec45ff577376c0d0178fdb2690134d4656488f46e129aba3cc5a +67 -0
- package/skills/project-update/history/blobs/7f063e79195a5c85c37b7260dd20c184d3b9bea85e37202e0bbca73ad84bba49 +13 -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/c8573ca1f810ce6c4ac49456a4c1725b93e0d3b74c54ce95d46d8747777895f0 +13 -0
- package/skills/project-update/history/blobs/ca4499d829268c8766fb3b825a515e88dc55c237e9d202816b50959b4dac031f +13 -0
- package/skills/project-update/history/blobs/d6e5b34d475e7187d2a8cb2aac7e7f1a427142baf6461bda387d70ceb4d8a8c1 +30 -0
- package/skills/project-update/history/blobs/e120ff4e32b2e950fc94ada4e787d57dc9c790da37223e5a202d74f7e3aadf80 +13 -0
- package/skills/project-update/history/blobs/e9adb7a9905eb719ac78ab96671f055da53e5001810f5ad6d6c94306b0323fe7 +74 -0
- package/skills/project-update/history/blobs/fae3064e648cdc55be4aeb1177525b302a5e34ebd752e4a904f47dc0f4557b96 +71 -0
- package/skills/project-update/history/index.json +60 -20
- 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/0011_retire_risk_tiers.py +37 -0
- package/skills/project-update/migrations/README.md +2 -1
- package/skills/project-update/migrations/__init__.py +6 -1
- package/skills/project-update/render_instructions.py +1 -1
- package/skills/project-update/update.py +385 -89
- package/skills/rr-build/SKILL.md +14 -5
- package/skills/rr-build-all/SKILL.md +190 -0
- package/skills/rr-plan/SKILL.md +16 -4
- package/skills/rr-review/SKILL.md +24 -9
- package/skills/rr-setup/SKILL.md +15 -2
- package/skills/rr-setup/setup.py +7 -1
- package/skills/rr-ship/SKILL.md +16 -8
- package/skills/rr-spec/SKILL.md +4 -2
- package/skills/rr-status/SKILL.md +2 -2
- package/skills/usage-report/usage-report.py +45 -10
- package/skills/ai-init/templates/.ai/policies/risk-tiers.json +0 -628
- package/skills/ai-init/templates/.ai/policies/risk-tiers.md +0 -180
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
|
|
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, as the harness recorded; else a human runs it outside.
|
|
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 -->
|
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
# SDLC artefacts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Each stage has a skill that produces one file here,
|
|
3
|
+
Work that needs a written intent and design before any code follows
|
|
4
|
+
**spec → plan → build**. Each stage has a skill that produces one file here,
|
|
5
|
+
and the next stage reads it.
|
|
5
6
|
|
|
6
7
|
```
|
|
7
|
-
/
|
|
8
|
+
/rr-spec <idea | intent/<slug>.md> → docs/sdlc/specs/<slug>.md (## Intent, requirements, design, Flagged concerns)
|
|
9
|
+
│ docs/sdlc/intent/<slug>.questions.md (the brainstorm, recorded by state.py)
|
|
10
|
+
/rr-plan specs/<slug>.md → docs/sdlc/plans/<slug>.md (files that change, order, risks, proof, rollback)
|
|
8
11
|
│
|
|
9
|
-
/
|
|
10
|
-
|
|
11
|
-
/sdlc-plan specs/<slug>.md → docs/sdlc/plans/<slug>.md (files, order, risks, proof)
|
|
12
|
-
│
|
|
13
|
-
build (main session, one plan step at a time; `ai-reviewer` before commit)
|
|
12
|
+
/rr-build — one row at a time (each row is one task, weighed and started by /rr-plan)
|
|
13
|
+
/rr-build-all plans/<slug>.md — the whole plan as one task: parallel implementers, one review loop
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
| Folder | Produced by | Contains |
|
|
17
17
|
|---|---|---|
|
|
18
|
-
| `intent/` | `/
|
|
19
|
-
| `specs/` | `/
|
|
20
|
-
| `plans/` | `/
|
|
21
|
-
| `adr/` | you
|
|
18
|
+
| `intent/` | `/rr-spec`'s brainstorm (`state.py ask --topic`) | `<slug>.questions.md`, one question at a time with the answer on record; an `intent/<slug>.md` written by hand is accepted instead of the brainstorm |
|
|
19
|
+
| `specs/` | `/rr-spec`, `architect` on STRONG | `## Intent` (problem, outcome, affected users and systems, constraints, decisions taken), requirements, design; ends with **Flagged concerns**, which a human resolves or accepts before planning |
|
|
20
|
+
| `plans/` | `/rr-plan` | Files that change / Order of work / Risks / Proof (every requirement to a test or command) / Rollback; each row carries its files and its *Consumes* line |
|
|
21
|
+
| `adr/` | you, or `architect` | Architecture Decision Records (MADR style) |
|
|
22
|
+
| `constitution.md` | `/rr-setup`, then you | the principles the project does not negotiate, cited as `C<n>` in specs, plans and reviews; at most 15 lines |
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
The skills fill in the plugin's templates
|
|
25
|
+
(`$AI_HOME/skills/project-init/templates/{intent,spec,plan,adr}.md`, in the
|
|
26
|
+
installed plugin); to override one, copy it to `docs/sdlc/<folder>/TEMPLATE.md`
|
|
27
|
+
and edit it — the skills read that one first. Commit the artefacts with the code they describe, so the
|
|
28
|
+
reasoning survives the pull request. A task's own record — weight, plan, questions, verify runs, review,
|
|
29
|
+
verdict — is not here but in `.ai/reports/<task-id>/task.md`, written by
|
|
30
|
+
`state.py`.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Testing policy
|
|
2
|
+
|
|
3
|
+
Tests describe behaviour. Coverage percentage is a side effect, not a goal.
|
|
4
|
+
|
|
5
|
+
## Characterization first
|
|
6
|
+
|
|
7
|
+
Before changing behaviour that is not covered by a test — which is most legacy
|
|
8
|
+
behaviour — write a test that passes against the code **as it is today**. That
|
|
9
|
+
test is the definition of what must not break. Only then change the code.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
characterization test → refactor → verify identical behaviour → feature change
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Never do those in one step, and never in one commit.
|
|
16
|
+
|
|
17
|
+
## What to consider, by kind of change
|
|
18
|
+
|
|
19
|
+
| Change touches | Also test |
|
|
20
|
+
|---|---|
|
|
21
|
+
| a shared service | the existing callers, not only the new one |
|
|
22
|
+
| money, tax, fiscal | rounding, currency, negative and zero amounts, refunds |
|
|
23
|
+
| a state machine | every transition that is now reachable, and the ones that must stay unreachable |
|
|
24
|
+
| an async consumer | retry, double delivery, out-of-order delivery, poison message |
|
|
25
|
+
| an external integration | timeout, 500, malformed response, partial write |
|
|
26
|
+
| a write path | idempotency, concurrency, transaction boundaries |
|
|
27
|
+
| authorization | the negative case, for every role |
|
|
28
|
+
| a public API or event | the old shape still works |
|
|
29
|
+
|
|
30
|
+
## Three scopes, three moments
|
|
31
|
+
|
|
32
|
+
A task runs tests three times at most, and each run has its own scope. Nothing
|
|
33
|
+
else runs in between.
|
|
34
|
+
|
|
35
|
+
| When | What runs | Command |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| after each implementation step, before `step-done` | **only the tests the plan named for that step** — the files it touched, their direct callers' tests | `step_test_command` |
|
|
38
|
+
| once, after the **last** step | the full fast suite — unit and integration, no e2e | `verify_command` |
|
|
39
|
+
| once, at the **end of the task**, after the full suite is green | the end-to-end suite | `e2e_command` |
|
|
40
|
+
|
|
41
|
+
- A step never runs the full suite, and **never** runs e2e. If the plan named
|
|
42
|
+
no test for a step, nothing runs for it.
|
|
43
|
+
- A step's tests are scoped by path or filter, not by `--stop-on-failure`: the
|
|
44
|
+
scoped run still goes to the end, and its failures are fixed inside that step.
|
|
45
|
+
- The e2e suite runs **once per task**, at the very end, never per step. Skip it
|
|
46
|
+
at T0/T1, and when the change cannot reach a flow the e2e suite covers — say
|
|
47
|
+
which, in one line; silence is not a verdict.
|
|
48
|
+
- A failure in a step's own tests is fixed in that step, before `step-done`;
|
|
49
|
+
the batch rule below is for the two end-of-task runs.
|
|
50
|
+
- After a green suite, a documents-only edit runs `doc_tests` (step scope), not the suite.
|
|
51
|
+
|
|
52
|
+
## Reading a failure
|
|
53
|
+
|
|
54
|
+
**Run to the end, then fix as one batch.** The verification command runs with
|
|
55
|
+
no fail-fast flag and is never interrupted at the first failure. Collect every
|
|
56
|
+
failure, classify each one, fix all the new regressions in one batch
|
|
57
|
+
(`state.py start --amend` widens the task when a fix needs more files), and run the
|
|
58
|
+
command once more. One failure never
|
|
59
|
+
sends the task back to the start: the plan, the tier and the finished steps
|
|
60
|
+
stand. At most two remediation rounds; a third means the human decides.
|
|
61
|
+
|
|
62
|
+
Classify before fixing. The four answers are:
|
|
63
|
+
|
|
64
|
+
- **EXISTING TEST FAILURE** — it failed before this change too. Say so, do not
|
|
65
|
+
fix it inside this task.
|
|
66
|
+
- **NEW REGRESSION** — this change broke it. Fix the code, not the test.
|
|
67
|
+
- **TEST ENVIRONMENT FAILURE** — database, fixtures, network, container. Say what
|
|
68
|
+
is missing.
|
|
69
|
+
- **UNKNOWN** — you could not tell. Say what you tried.
|
|
70
|
+
|
|
71
|
+
A review's MEDIUM and LOW findings are recorded with `state.py memory risk add`, not
|
|
72
|
+
fixed in the task; only a BLOCKER, a HIGH or a regression buys an `--after-fix` run.
|
|
73
|
+
|
|
74
|
+
**Never edit a test to make it pass when production behaviour changed
|
|
75
|
+
unexpectedly.** A test that suddenly disagrees with the code is evidence, and
|
|
76
|
+
deleting evidence is the worst available option.
|
|
77
|
+
|
|
78
|
+
## Verification
|
|
79
|
+
|
|
80
|
+
Three commands, one per scope. `verify_command` proves the project is healthy;
|
|
81
|
+
`step_test_command` proves one step is; `e2e_command` proves the whole flow
|
|
82
|
+
still works. Keep each to one command — chain the pieces in a Makefile or
|
|
83
|
+
composer script if there are several — and make sure none of them stops at the
|
|
84
|
+
first failure (`--stop-on-failure`, `-x`, `--bail`, `failfast` are off).
|
|
85
|
+
|
|
86
|
+
`verify_command` must **exclude** the e2e suite: it is the fast loop run after
|
|
87
|
+
the last step and again after a remediation batch. `e2e_command` runs once, at
|
|
88
|
+
the end of the task. A bugfix still shows its new failing test first — that one
|
|
89
|
+
test only, through `single_test`.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
verify_command: # the full fast suite, e2e excluded — e.g. make test | composer qa && vendor/bin/phpunit --exclude-group e2e
|
|
93
|
+
step_test_command: # the same runner, scoped to a path or filter — e.g. vendor/bin/phpunit {files} | pytest {files}
|
|
94
|
+
e2e_command: # the end-to-end suite, run once at the end of a task — e.g. make e2e | vendor/bin/behat | npx playwright test
|
|
95
|
+
lint_command: # e.g. vendor/bin/php-cs-fixer fix --dry-run | ruff check . | npm run lint — write `none` if the project has none
|
|
96
|
+
doc_tests: # the suites a documents-only edit runs after a green suite — e.g. tests/test_docs.py; `none` if the project has none
|
|
97
|
+
typecheck_command: # e.g. vendor/bin/phpstan analyse --no-progress | mypy . | npx tsc --noEmit — `none` if the project has none
|
|
98
|
+
healthy_output: # two or three lines of what a green run ends with
|
|
99
|
+
runtime: # roughly, for each command, so a hang is recognisable
|
|
100
|
+
single_test: # how to run one test, for the failing-test-first step of a bugfix
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`step_test_command` may contain `{files}`, which is replaced by the paths the
|
|
104
|
+
step names; without it the paths are appended. The placeholder is what lets
|
|
105
|
+
`sensors.py` run a step's own tests for the "test must bite" check, so a runner
|
|
106
|
+
that only takes a `--filter` gets `bite: unavailable` rather than a wrong answer.
|
|
107
|
+
|
|
108
|
+
`bite_codes` (optional) lists the exit codes that mean "a test failed" when a
|
|
109
|
+
test is shown failing before its fix; the default is `1`. Runners that exit
|
|
110
|
+
differently say so here — `bite_codes: 1,2` for pytest (2 is a collection
|
|
111
|
+
error, such as a missing import), `1,2` for phpunit, `101` for cargo. Any other
|
|
112
|
+
code is the runner's own usage error and does not count as a bite.
|
|
113
|
+
|
|
114
|
+
`lint_command` and `typecheck_command` are read by the deterministic sensors,
|
|
115
|
+
never by a model. They decide, together with the test run, whether a light-mode change
|
|
116
|
+
can skip its model review (`state.py close` measures them). A **missing** line is
|
|
117
|
+
`unavailable` and keeps the review; only the literal `none`, written by a human,
|
|
118
|
+
says the project has no such tool. Read the exact line from the project's
|
|
119
|
+
config files (`package.json`, `composer.json`, `pyproject.toml`, `Makefile`), and
|
|
120
|
+
never run a command just to find it out.
|
|
121
|
+
|
|
122
|
+
If the project has no e2e suite, write `e2e_command: none` and say so — an
|
|
123
|
+
empty line is read as "not written down yet" and the next task will go looking
|
|
124
|
+
for it. Say which group, tag or directory marks the e2e tests and how
|
|
125
|
+
`verify_command` excludes them, so a step's scoped run and the fast suite stay
|
|
126
|
+
free of them.
|
|
127
|
+
|
|
128
|
+
## Project specifics
|
|
129
|
+
|
|
130
|
+
<!-- The real commands: how to run unit tests, integration tests, a single test.
|
|
131
|
+
Where fixtures live. What the CI runs. Cite the config. -->
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# SDLC artefacts
|
|
2
|
+
|
|
3
|
+
Work that needs a written intent and design before any code follows
|
|
4
|
+
**spec → plan → build**. Each stage has a skill that produces one file here,
|
|
5
|
+
and the next stage reads it.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/rr-spec <idea | intent/<slug>.md> → docs/sdlc/specs/<slug>.md (## Intent, requirements, design, Flagged concerns)
|
|
9
|
+
│ docs/sdlc/intent/<slug>.questions.md (the brainstorm, recorded by state.py)
|
|
10
|
+
/rr-plan specs/<slug>.md → docs/sdlc/plans/<slug>.md (files that change, order, risks, proof, rollback)
|
|
11
|
+
│
|
|
12
|
+
/rr-build — one row at a time (each row is one task, weighed and started by /rr-plan)
|
|
13
|
+
/rr-build-all plans/<slug>.md — the whole plan as one task: parallel implementers, one review loop
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
| Folder | Produced by | Contains |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `intent/` | `/rr-spec`'s brainstorm (`state.py ask --topic`) | `<slug>.questions.md`, one question at a time with the answer on record; an `intent/<slug>.md` written by hand is accepted instead of the brainstorm |
|
|
19
|
+
| `specs/` | `/rr-spec`, `architect` on STRONG | `## Intent` (problem, outcome, affected users and systems, constraints, decisions taken), requirements, design; ends with **Flagged concerns**, which a human resolves or accepts before planning |
|
|
20
|
+
| `plans/` | `/rr-plan` | Files that change / Order of work / Risks / Proof (every requirement to a test or command) / Rollback; each row carries its files and its *Consumes* line |
|
|
21
|
+
| `adr/` | you, or `architect` | Architecture Decision Records (MADR style) |
|
|
22
|
+
| `constitution.md` | `/rr-setup`, then you | the principles the project does not negotiate, cited as `C<n>` in specs, plans and reviews; at most 15 lines |
|
|
23
|
+
|
|
24
|
+
The skills fill in the plugin's templates
|
|
25
|
+
(`skills/project-init/templates/{spec,plan,adr}.md`); a folder holds a
|
|
26
|
+
`TEMPLATE.md` only when this project overrides the plugin's, and the skills read
|
|
27
|
+
that one first. Commit the artefacts with the code they describe, so the
|
|
28
|
+
reasoning survives the pull request. A task's own record — weight, plan, questions, verify runs, review,
|
|
29
|
+
verdict — is not here but in `.ai/reports/<task-id>/task.md`, written by
|
|
30
|
+
`state.py`.
|
|
@@ -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 and the harness records, else a human. When a review flags a mistake twice, the fix goes in 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,123 @@
|
|
|
1
|
+
# Testing policy
|
|
2
|
+
|
|
3
|
+
Tests describe behaviour. Coverage percentage is a side effect, not a goal.
|
|
4
|
+
|
|
5
|
+
## Characterization first
|
|
6
|
+
|
|
7
|
+
Before changing behaviour that is not covered by a test — which is most legacy
|
|
8
|
+
behaviour — write a test that passes against the code **as it is today**. That
|
|
9
|
+
test is the definition of what must not break. Only then change the code.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
characterization test → refactor → verify identical behaviour → feature change
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Never do those in one step, and never in one commit.
|
|
16
|
+
|
|
17
|
+
## What to consider, by kind of change
|
|
18
|
+
|
|
19
|
+
| Change touches | Also test |
|
|
20
|
+
|---|---|
|
|
21
|
+
| a shared service | the existing callers, not only the new one |
|
|
22
|
+
| money, tax, fiscal | rounding, currency, negative and zero amounts, refunds |
|
|
23
|
+
| a state machine | every transition that is now reachable, and the ones that must stay unreachable |
|
|
24
|
+
| an async consumer | retry, double delivery, out-of-order delivery, poison message |
|
|
25
|
+
| an external integration | timeout, 500, malformed response, partial write |
|
|
26
|
+
| a write path | idempotency, concurrency, transaction boundaries |
|
|
27
|
+
| authorization | the negative case, for every role |
|
|
28
|
+
| a public API or event | the old shape still works |
|
|
29
|
+
|
|
30
|
+
## Three scopes, three moments
|
|
31
|
+
|
|
32
|
+
A task runs tests three times at most, and each run has its own scope. Nothing
|
|
33
|
+
else runs in between.
|
|
34
|
+
|
|
35
|
+
| When | What runs | Command |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| after each implementation step, before `step-done` | **only the tests the plan named for that step** — the files it touched, their direct callers' tests | `step_test_command` |
|
|
38
|
+
| once, after the **last** step | the full fast suite — unit and integration, no e2e | `verify_command` |
|
|
39
|
+
| once, at the **end of the task**, after the full suite is green | the end-to-end suite | `e2e_command` |
|
|
40
|
+
|
|
41
|
+
- A step never runs the full suite, and **never** runs e2e. If the plan named
|
|
42
|
+
no test for a step, nothing runs for it.
|
|
43
|
+
- A step's tests are scoped by path or filter, not by `--stop-on-failure`: the
|
|
44
|
+
scoped run still goes to the end, and its failures are fixed inside that step.
|
|
45
|
+
- The e2e suite runs **once per task**, at the very end, never per step. Skip it
|
|
46
|
+
at T0/T1, and when the change cannot reach a flow the e2e suite covers — say
|
|
47
|
+
which, in one line; silence is not a verdict.
|
|
48
|
+
- A failure in a step's own tests is fixed in that step, before `step-done`;
|
|
49
|
+
the batch rule below is for the two end-of-task runs.
|
|
50
|
+
- After a green suite, a documents-only edit runs `doc_tests` (step scope), not the suite.
|
|
51
|
+
|
|
52
|
+
## Reading a failure
|
|
53
|
+
|
|
54
|
+
**Run to the end, then fix as one batch.** The verification command runs with
|
|
55
|
+
no fail-fast flag and is never interrupted at the first failure. Collect every
|
|
56
|
+
failure, classify each one, fix all the new regressions in a single remediation
|
|
57
|
+
step (`state.py remediate`), and run the command once more. One failure never
|
|
58
|
+
sends the task back to the start: the plan, the tier and the finished steps
|
|
59
|
+
stand. At most two remediation rounds; a third means the human decides.
|
|
60
|
+
|
|
61
|
+
Classify before fixing. The four answers are:
|
|
62
|
+
|
|
63
|
+
- **EXISTING TEST FAILURE** — it failed before this change too. Say so, do not
|
|
64
|
+
fix it inside this task.
|
|
65
|
+
- **NEW REGRESSION** — this change broke it. Fix the code, not the test.
|
|
66
|
+
- **TEST ENVIRONMENT FAILURE** — database, fixtures, network, container. Say what
|
|
67
|
+
is missing.
|
|
68
|
+
- **UNKNOWN** — you could not tell. Say what you tried.
|
|
69
|
+
|
|
70
|
+
A review's MEDIUM and LOW findings are recorded with `state.py memory risk add`, not
|
|
71
|
+
fixed in the task; only a BLOCKER, a HIGH or a regression buys an `--after-fix` run.
|
|
72
|
+
|
|
73
|
+
**Never edit a test to make it pass when production behaviour changed
|
|
74
|
+
unexpectedly.** A test that suddenly disagrees with the code is evidence, and
|
|
75
|
+
deleting evidence is the worst available option.
|
|
76
|
+
|
|
77
|
+
## Verification
|
|
78
|
+
|
|
79
|
+
Three commands, one per scope. `verify_command` proves the project is healthy;
|
|
80
|
+
`step_test_command` proves one step is; `e2e_command` proves the whole flow
|
|
81
|
+
still works. Keep each to one command — chain the pieces in a Makefile or
|
|
82
|
+
composer script if there are several — and make sure none of them stops at the
|
|
83
|
+
first failure (`--stop-on-failure`, `-x`, `--bail`, `failfast` are off).
|
|
84
|
+
|
|
85
|
+
`verify_command` must **exclude** the e2e suite: it is the fast loop run after
|
|
86
|
+
the last step and again after a remediation batch. `e2e_command` runs once, at
|
|
87
|
+
the end of the task. A bugfix still shows its new failing test first — that one
|
|
88
|
+
test only, through `single_test`.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
verify_command: # the full fast suite, e2e excluded — e.g. make test | composer qa && vendor/bin/phpunit --exclude-group e2e
|
|
92
|
+
step_test_command: # the same runner, scoped to a path or filter — e.g. vendor/bin/phpunit {files} | pytest {files}
|
|
93
|
+
e2e_command: # the end-to-end suite, run once at the end of a task — e.g. make e2e | vendor/bin/behat | npx playwright test
|
|
94
|
+
lint_command: # e.g. vendor/bin/php-cs-fixer fix --dry-run | ruff check . | npm run lint — write `none` if the project has none
|
|
95
|
+
doc_tests: # the suites a documents-only edit runs after a green suite — e.g. tests/test_docs.py; `none` if the project has none
|
|
96
|
+
typecheck_command: # e.g. vendor/bin/phpstan analyse --no-progress | mypy . | npx tsc --noEmit — `none` if the project has none
|
|
97
|
+
healthy_output: # two or three lines of what a green run ends with
|
|
98
|
+
runtime: # roughly, for each command, so a hang is recognisable
|
|
99
|
+
single_test: # how to run one test, for the failing-test-first step of a bugfix
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`step_test_command` may contain `{files}`, which is replaced by the paths the
|
|
103
|
+
step names; without it the paths are appended. The placeholder is what lets
|
|
104
|
+
`sensors.py` run a step's own tests for the "test must bite" check, so a runner
|
|
105
|
+
that only takes a `--filter` gets `bite: unavailable` rather than a wrong answer.
|
|
106
|
+
|
|
107
|
+
`lint_command` and `typecheck_command` are read by the deterministic sensors,
|
|
108
|
+
never by a model. They decide, together with the test run, whether a T2 change
|
|
109
|
+
can skip its model review (`risk-tiers.json`, `sensors`). A **missing** line is
|
|
110
|
+
`unavailable` and keeps the review; only the literal `none`, written by a human,
|
|
111
|
+
says the project has no such tool. `sensors.py detect` proposes the exact line
|
|
112
|
+
from the config files it finds, and never runs what it detected.
|
|
113
|
+
|
|
114
|
+
If the project has no e2e suite, write `e2e_command: none` and say so — an
|
|
115
|
+
empty line is read as "not written down yet" and the next task will go looking
|
|
116
|
+
for it. Say which group, tag or directory marks the e2e tests and how
|
|
117
|
+
`verify_command` excludes them, so a step's scoped run and the fast suite stay
|
|
118
|
+
free of them.
|
|
119
|
+
|
|
120
|
+
## Project specifics
|
|
121
|
+
|
|
122
|
+
<!-- The real commands: how to run unit tests, integration tests, a single test.
|
|
123
|
+
Where fixtures live. What the CI runs. Cite the config. -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "version": 1, "enabled": true, "window_seconds": 120, "stop_blocks_max": 2 }
|
|
@@ -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, as the harness recorded; else a human runs it outside.
|
|
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,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 @@
|
|
|
1
|
+
11
|
|
@@ -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 and the harness records, 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`.
|
|
13
|
+
<!-- riskrails:end -->
|
|
@@ -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"}
|