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/rr-build/SKILL.md
CHANGED
|
@@ -38,6 +38,8 @@ $STATE handoff --print
|
|
|
38
38
|
Trust it over a compaction summary; it is rendered from the state. If
|
|
39
39
|
`$ARGUMENTS` asks for something else, say what is in flight and ask whether to
|
|
40
40
|
finish it, or drop it with `$STATE close --abandon`. Never discard it silently.
|
|
41
|
+
A handoff line `Batch: on` means the task runs the loop of `/rr-build-all` —
|
|
42
|
+
parallel implementers and fixers, its own budgets — continue there.
|
|
41
43
|
- **`Handed to <the other runtime>`:** every state change exits 7 until that
|
|
42
44
|
runtime resumes it. Say where to resume; take it back with
|
|
43
45
|
`$STATE handoff --to <this runtime>` only when asked. `Handed to <this
|
|
@@ -47,6 +49,11 @@ $STATE handoff --print
|
|
|
47
49
|
|
|
48
50
|
## 2. Edit, inside the task
|
|
49
51
|
|
|
52
|
+
Before the first edit, recall what the record already holds about the task's
|
|
53
|
+
files — open risks, residual findings, decisions and why, within a token budget,
|
|
54
|
+
read-only: `$STATE recall --files "<the task's files>"` (`memory show <id>` for
|
|
55
|
+
one task in full).
|
|
56
|
+
|
|
50
57
|
Implement it yourself. The scope guard refuses an edit to a file the task does
|
|
51
58
|
not name. That refusal is the plan being enforced, not an obstacle:
|
|
52
59
|
|
|
@@ -56,8 +63,9 @@ $STATE start --amend --files "+<path>" --why "<why the task needs it>"
|
|
|
56
63
|
```
|
|
57
64
|
|
|
58
65
|
The amendment is journaled; the task is not re-planned. A file that belongs to
|
|
59
|
-
a different change is not amended in — it is
|
|
60
|
-
|
|
66
|
+
a different change is not amended in — it is recorded with `$STATE memory risk
|
|
67
|
+
add --title … --where "file:line" --label RISK --what …` (in files mode that
|
|
68
|
+
writes `.ai/project/known-risks.md`) and left for its own task.
|
|
61
69
|
|
|
62
70
|
Production behaviour is the source of truth: code that looks wrong may be
|
|
63
71
|
load-bearing. Change only what the task asked for; a refactoring never rides
|
|
@@ -94,7 +102,7 @@ at `close` is finished by `close`.
|
|
|
94
102
|
|
|
95
103
|
## 4. Verify once, to the end
|
|
96
104
|
|
|
97
|
-
After the last edit:
|
|
105
|
+
After the last edit (after the last step: `$STATE step-done <id> && $STATE verify --scope all`):
|
|
98
106
|
|
|
99
107
|
```bash
|
|
100
108
|
$STATE verify --scope all
|
|
@@ -118,10 +126,11 @@ fixed:
|
|
|
118
126
|
$STATE verify --scope all --after-fix "<n> regressions: <one line each>"
|
|
119
127
|
```
|
|
120
128
|
|
|
121
|
-
- An existing failure is recorded
|
|
129
|
+
- An existing failure is recorded with `$STATE memory risk add`, not fixed.
|
|
122
130
|
- An environment failure gets one more run, not a code change.
|
|
123
131
|
- Never edit a test to make it pass.
|
|
124
|
-
- Two whole-suite runs are the budget (`tests.max_suite_runs
|
|
132
|
+
- Two whole-suite runs are the budget (`tests.max_suite_runs`; in a batch task
|
|
133
|
+
`batch.max_suite_runs`). The third
|
|
125
134
|
exits 6 `SUITE_BUDGET`: running it again is the human's decision, from their
|
|
126
135
|
own terminal, with the command it prints — or the pick: with the dialog on it
|
|
127
136
|
also prints `DIALOG_PENDING`, and you run the command the human picks,
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rr-build-all
|
|
3
|
+
description: Run a whole programme plan as one task in a repository that has .ai/ — every row of docs/sdlc/plans/<slug>.md becomes a step with its files, tests and dependencies; the ready steps go to parallel implementer subagents on the BALANCED tier, serialized where one depends on another; after the last step the suite runs once to the end, every failure is recorded and fixed by parallel fixers as one batch, then one review at the mode's tier, its findings fixed by parallel fixers as one batch, then tests and the review again until it passes; the pass past the batch budget (three failed reviews) is the human's. Use on "/rr-build-all", "run the whole plan", "all points in one task", "sdlc-lite", "batch build".
|
|
4
|
+
argument-hint: [<path to docs/sdlc/plans/<slug>.md>] | --resume
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /rr-build-all $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
One task for the whole plan — the loop a developer otherwise runs by hand: implement every
|
|
10
|
+
row, test once, fix everything, review, fix everything, again, until the review passes. The
|
|
11
|
+
mode (light, guarded, full) is still the weight's; batch only changes who implements, how
|
|
12
|
+
failures are fixed, and how many rounds run before the human. Resolve the install root once:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
for AI_HOME in "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${CODEX_HOME:-$HOME/.codex}"; do
|
|
16
|
+
[ -d "$AI_HOME/skills/rr-build-all" ] && break
|
|
17
|
+
done
|
|
18
|
+
STATE="python3 $AI_HOME/skills/ai-task/state.py"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Shared files resolve through `"$AI_HOME/riskrails/ai-path" <path>`. Read `policies/safety.md`
|
|
22
|
+
and `using-riskrails.md` once per session. Every rule of `/rr-build` holds here: nothing outside
|
|
23
|
+
the task's files, no commit, a refusal names the way out and is never reworded around.
|
|
24
|
+
|
|
25
|
+
## 1. Resume or start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
$STATE handoff --print
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **A task in flight whose handoff says `Batch: on`:** continue at its stage — §3 while
|
|
32
|
+
`steps --ready` prints a step, §4 after the last `step-done`, §5 after a green verify.
|
|
33
|
+
- **A task in flight without that line:** it runs the standard way — say so and hand over to
|
|
34
|
+
`/rr-build`. Only the human turns the loop on mid-task (`batch on`, §Switching).
|
|
35
|
+
- **No task in flight:** §2.
|
|
36
|
+
|
|
37
|
+
## 2. The plan → one task
|
|
38
|
+
|
|
39
|
+
`$ARGUMENTS` is a programme plan (default: the newest `docs/sdlc/plans/*.md`). Its spec's
|
|
40
|
+
**Flagged concerns** are resolved or accepted, as `/rr-plan` demands; a plan with an open
|
|
41
|
+
blocking question is not started.
|
|
42
|
+
|
|
43
|
+
1. **Rows → steps.** Every row of **Order of work** that an agent does becomes one step
|
|
44
|
+
`{"id", "files", "tests", "after"}`: the row's files, the tests it names, and in `after` the
|
|
45
|
+
rows it depends on — its *Consumes* line, a stated dependency, or an earlier row it builds
|
|
46
|
+
on. A refactoring row stays its own task (C3): leave it out and say so. A human row (G1, …)
|
|
47
|
+
is not a step: every row after it names in `after` every step before it, so nothing past
|
|
48
|
+
the human row is ready until the rows before it are done — and its question (3.) answered.
|
|
49
|
+
2. **Weigh and start** — `/rr-plan`'s "A task" section, on the union of the steps' files, then:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
$STATE start --goal "<the plan's title>" --workflow feature --files "<every file of every step>" \
|
|
53
|
+
--tests "<every test>" [--declare …] --plan-file /path/steps.json \
|
|
54
|
+
--batch --by "<the human's name>" --why "/rr-build-all docs/sdlc/plans/<slug>.md" \
|
|
55
|
+
[--mode <higher> --by "<name>"]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
An `after` id must exist and form no cycle; two steps that name one file are ordered by
|
|
59
|
+
`start` itself (`ordered: s4 after s1 (shares …)`), so two implementers never hold one file.
|
|
60
|
+
In full mode the written plan is the programme plan the human already accepted. With
|
|
61
|
+
`batch.default` true in `.ai/policies/weight.json` the flag is implied; `--no-batch` opts out.
|
|
62
|
+
3. **A human row** becomes a question the moment every step before it is done — asked
|
|
63
|
+
**before** the next wave is dispatched:
|
|
64
|
+
`$STATE ask "G1 is yours: <what>" --option "A: done" --option "B: not yet" --recommend A`,
|
|
65
|
+
then **stop**. Never dispatch while a question is pending (`step-done` and `close` exit 4;
|
|
66
|
+
`steps --ready` still answers); `/rr-build-all --resume` goes on once the human answered.
|
|
67
|
+
|
|
68
|
+
## 3. Implement, by waves
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
$STATE steps --ready # the next wave: the steps whose dependencies are done, one id per line
|
|
72
|
+
$STATE steps --json # batch_policy.tier and .parallel: the implementers' tier, how many at once
|
|
73
|
+
$STATE profile --tier <that tier> # the tier's model in this runtime (BALANCED as shipped)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Resuming mid-wave** (after a compaction or a stop): before dispatching, compare
|
|
77
|
+
`git status --short` with each ready step's files. A ready step whose files already changed
|
|
78
|
+
was dispatched before and never recorded — do not send it again blind: brief its implementer
|
|
79
|
+
with that partial diff to finish, or `step-done` it if its tests pass.
|
|
80
|
+
|
|
81
|
+
Dispatch one subagent per ready step, at most `batch.parallel` (shipped 3) at once — under
|
|
82
|
+
Claude Code a `general-purpose` agent with `model:` set to that tier's model; under Codex a
|
|
83
|
+
spawn with no agent named, on that model. Its brief is the implementer contract,
|
|
84
|
+
`agents/implementer.md` in the shared tree, filled in:
|
|
85
|
+
|
|
86
|
+
- **Goal** — the row's text from the plan. **Allowed files** — the step's files, nothing else.
|
|
87
|
+
**Forbidden files** — every other step's files, "another implementer holds them".
|
|
88
|
+
**Required behaviour / behaviour that must not change** — from the row and the spec.
|
|
89
|
+
**Required tests** — the step's tests.
|
|
90
|
+
- Its own tests, run directly: `step_test_command` from `.ai/policies/testing.md` with the
|
|
91
|
+
step's tests in place of `{files}`. **A subagent never calls `state.py`** — one state file,
|
|
92
|
+
one writer, this session.
|
|
93
|
+
- It returns `## RESULT`, or `## SCOPE_CHANGE_REQUIRED`, or `## QUESTIONS_NEEDED`.
|
|
94
|
+
|
|
95
|
+
After the wave, in this session:
|
|
96
|
+
|
|
97
|
+
- `RESULT` → `$STATE step-done <id>` for each step. Exit 6 names a file outside the task:
|
|
98
|
+
`$STATE start --amend --files "+<path>" --why "…"`, then `step-done` again.
|
|
99
|
+
- `SCOPE_CHANGE_REQUIRED` → amend as above and send the same step again. A file that belongs
|
|
100
|
+
to another change goes into `.ai/project/known-risks.md`, not into the task.
|
|
101
|
+
- `QUESTIONS_NEEDED` → `$STATE ask --batch /path/questions.json`, show the questions, **stop**;
|
|
102
|
+
the human answers, and `/rr-build-all --resume` goes on from the handoff.
|
|
103
|
+
|
|
104
|
+
Then `steps --ready` again. Nothing ready and nothing pending: §4. Keep this session small:
|
|
105
|
+
read `## RESULT` blocks, never a subagent's diff; `git status --short` is the only view of the
|
|
106
|
+
tree you need between waves.
|
|
107
|
+
|
|
108
|
+
## 4. Tests once, failures fixed as one parallel batch
|
|
109
|
+
|
|
110
|
+
After the last wave, `$STATE step-done <id> && $STATE verify --scope all`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
$STATE verify --scope all
|
|
114
|
+
$STATE verify --scope all --e2e not-applicable "<why the change reaches no e2e flow>"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Green: §5. **Red:** a FAST `log-reader` reads the log by path and returns every failure
|
|
118
|
+
classified — new regression, existing failure, environment, unknown (`testing.md`, "Reading a
|
|
119
|
+
failure") — each with `file:line`. Then:
|
|
120
|
+
|
|
121
|
+
- an existing failure → one entry in `.ai/project/known-risks.md`; an environment failure →
|
|
122
|
+
one more run, no code change;
|
|
123
|
+
- the new regressions, grouped by the file they point at → one fixer subagent per group, in
|
|
124
|
+
parallel, at most `batch.parallel`, on the batch tier, briefed with the failures' excerpts
|
|
125
|
+
and the files it may touch (that group's files, inside the task's). Fix the code, never the
|
|
126
|
+
test; report `files_changed` and what stayed unfixed;
|
|
127
|
+
- one run: `$STATE verify --scope all --after-fix "<n> regressions: <one line each>"`.
|
|
128
|
+
|
|
129
|
+
Red again → the same once more. Exit 6 `SUITE_BUDGET` — past `batch.max_suite_runs`
|
|
130
|
+
(shipped 8) — is the human's run: the pick in the dialog, or the command it prints, in their
|
|
131
|
+
own terminal. **Stop.**
|
|
132
|
+
|
|
133
|
+
## 5. Review, findings fixed as one parallel batch, again until it passes
|
|
134
|
+
|
|
135
|
+
One `ai-reviewer` at the mode's tier, briefed as `/rr-review` §2 says: the whole diff since the
|
|
136
|
+
task's base, the task's files, the tests, the last verify log; unbounded "look for" in full
|
|
137
|
+
mode, the security focus when the domain axis is 2. Record it verbatim:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
$STATE review --verdict <pass|pass_with_findings|blockers_open> --tier <BALANCED|STRONG> \
|
|
141
|
+
[--focus security] --report - <<'EOF'
|
|
142
|
+
<the reviewer's FINDINGS and VERDICT, unchanged>
|
|
143
|
+
EOF
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- **pass** → §6.
|
|
147
|
+
- **pass_with_findings** → the in-scope HIGHs, grouped by file, one fixer per group in
|
|
148
|
+
parallel; `$STATE verify --scope all --after-fix "H1,H2: …"`; `$STATE review --closes "H1,H2"`
|
|
149
|
+
→ §6. MEDIUM and LOW: `memory risk add` as `/rr-review` §4 says, for every verdict; only a
|
|
150
|
+
BLOCKER, a HIGH or a regression buys an `--after-fix` run.
|
|
151
|
+
- **blockers_open** → every in-scope BLOCKER and HIGH, grouped by file, one fixer per group in
|
|
152
|
+
parallel; `$STATE verify --scope all --after-fix "B1,H1: …"` (red: §4's batch); then
|
|
153
|
+
**§5 again, a full pass** — a fix batch across many files is a new change, not a scoped one.
|
|
154
|
+
|
|
155
|
+
Exit 6 `REVIEW_BUDGET` after `batch.review_rounds_max` passes (shipped 3 — three failed
|
|
156
|
+
reviews) — the next verdict is the human's: the pick in the dialog, or the command it prints,
|
|
157
|
+
in their own terminal. Show the open findings and **stop**. A `verify` past its budget inside
|
|
158
|
+
this loop is `SUITE_BUDGET`, handled the same way.
|
|
159
|
+
|
|
160
|
+
## 6. Ship
|
|
161
|
+
|
|
162
|
+
`/rr-ship` — `close` measures the two facts and, in full mode, asks the human to approve,
|
|
163
|
+
exactly as for any task; for a documents-only change since the green suite it is
|
|
164
|
+
`$STATE verify --scope step --files "<doc_tests>" && $STATE verify --scope e2e && $STATE close`. Suggest `/clear` after it.
|
|
165
|
+
|
|
166
|
+
## Switching
|
|
167
|
+
|
|
168
|
+
- **To the standard loop ("full" from here):** `$STATE batch off --by "<name>" --why "<why>"`.
|
|
169
|
+
The standard budgets apply from now, counting what was spent — with two passes behind you,
|
|
170
|
+
the next verdict is already the human's. The session implements the remaining steps itself;
|
|
171
|
+
continue with `/rr-build`, `/rr-review`, `/rr-ship`. Anyone may do this: it raises the rigor.
|
|
172
|
+
- **To batch, mid-task:** lowers the rigor — the human's, in their own terminal:
|
|
173
|
+
`python3 <AI_HOME>/skills/ai-task/state.py --root <project> batch on --by "<name>"`. Never
|
|
174
|
+
from a tool call; the refusal (`APPROVAL_REFUSED`) prints that command.
|
|
175
|
+
- **The project's default:** `batch.default` in `.ai/policies/weight.json`, beside
|
|
176
|
+
`batch.review_rounds_max`, `batch.max_suite_runs`, `batch.parallel` and `batch.tier`
|
|
177
|
+
(`/rr-setup --update` adds the section). A policy is the human's and is frozen during a task.
|
|
178
|
+
|
|
179
|
+
## Rules
|
|
180
|
+
|
|
181
|
+
- The mode is the weight's and only rises; the scope guard, the two facts, the gate and the
|
|
182
|
+
three nevers are untouched by batch.
|
|
183
|
+
- Three or more parallel agents run at BALANCED, never above — `start` refuses `batch.tier`
|
|
184
|
+
above it when `batch.parallel` is three or more; one reviewer per pass, at the mode's tier;
|
|
185
|
+
the session writes every conclusion (`model-routing.md`).
|
|
186
|
+
- One writer of the state: a subagent never calls `state.py`; this session records every step,
|
|
187
|
+
run and verdict.
|
|
188
|
+
- Failures and findings are fixed as batches, never one at a time; a test is never edited to
|
|
189
|
+
make it pass; what is outside the task is written down, not fixed.
|
|
190
|
+
- No commit, push, merge or deploy. The task ends at `/rr-ship`; approval is the human's.
|
package/skills/rr-plan/SKILL.md
CHANGED
|
@@ -24,7 +24,9 @@ first: a task already in flight is finished or abandoned before another starts.
|
|
|
24
24
|
|
|
25
25
|
When `$ARGUMENTS` is a spec (default: the newest `docs/sdlc/specs/*.md`):
|
|
26
26
|
|
|
27
|
-
1. `docs/sdlc/
|
|
27
|
+
1. `docs/sdlc/constitution.md` exists, else `/rr-setup`. The template is
|
|
28
|
+
`docs/sdlc/plans/TEMPLATE.md` when the project overrides it, else
|
|
29
|
+
`$AI_HOME/skills/project-init/templates/plan.md`. If the spec's
|
|
28
30
|
**Flagged concerns** holds unresolved items, list them and have the human
|
|
29
31
|
resolve or explicitly accept each one before planning.
|
|
30
32
|
2. If `superpowers:writing-plans` is available, use it for the step
|
|
@@ -38,7 +40,11 @@ When `$ARGUMENTS` is a spec (default: the newest `docs/sdlc/specs/*.md`):
|
|
|
38
40
|
4. Copy the template to `docs/sdlc/plans/<slug>.md`, fill **Files that change /
|
|
39
41
|
Order of work / Risks / Proof / Rollback**; Proof maps every requirement R<n>
|
|
40
42
|
to a test or command. One round of corrections; suggest the commit
|
|
41
|
-
`plan: <title>`. Each row of the plan is later one task, planned below
|
|
43
|
+
`plan: <title>`. Each row of the plan is later one task, planned below —
|
|
44
|
+
unless the whole plan runs as one task through `/rr-build-all` (the project's
|
|
45
|
+
`batch.default` in `weight.json`, or the developer's call): then every row
|
|
46
|
+
becomes one step of that task, so give each row its files and its *Consumes*
|
|
47
|
+
line (the rows it depends on) — that is what `/rr-build-all` reads.
|
|
42
48
|
|
|
43
49
|
## A task → weight, mode, plan, start
|
|
44
50
|
|
|
@@ -48,7 +54,10 @@ investigation — say which and why. `using-riskrails.md` in the shared tree
|
|
|
48
54
|
`workflows.md#<kind>` say what each kind requires. A refactoring and a feature
|
|
49
55
|
are never one task.
|
|
50
56
|
|
|
51
|
-
**2. Name the files** from the request, confirmed with `grep -n`.
|
|
57
|
+
**2. Name the files** from the request, confirmed with `grep -n`. Recall what
|
|
58
|
+
the record holds about them — `$STATE recall --files "<a>,<b>"`: open risks,
|
|
59
|
+
residual findings and earlier decisions are evidence for the weight and the
|
|
60
|
+
plan. Then weigh:
|
|
52
61
|
|
|
53
62
|
```bash
|
|
54
63
|
$STATE weight --files "<a>,<b>" --goal "<one sentence>" \
|
|
@@ -64,7 +73,7 @@ The table is `.ai/policies/weight.json` (`weight.md` mirrors it).
|
|
|
64
73
|
|
|
65
74
|
**3. The mode is the weight's**, not yours: raising it is free
|
|
66
75
|
(`--mode <m> --by "<name>"` on `start`); lowering it is the human's, from their
|
|
67
|
-
own terminal. Shape the plan by it:
|
|
76
|
+
own terminal, or the command they picked in the dialog. Shape the plan by it:
|
|
68
77
|
|
|
69
78
|
| Mode | Plan | Who sees it before the first edit |
|
|
70
79
|
|---|---|---|
|
|
@@ -92,6 +101,9 @@ $STATE start --goal "<one sentence>" --workflow <kind> --files "<a>,<b>" \
|
|
|
92
101
|
|
|
93
102
|
`--plan-file` is a JSON list `[{"id": "s1", "files": [...], "tests": [...]}]`
|
|
94
103
|
for a guarded or full plan with steps; a light task is one scope and needs none.
|
|
104
|
+
A step may add `"after": ["s0"]`, the steps it waits for — `/rr-build-all` runs
|
|
105
|
+
the rest in parallel — and `--batch --by "<name>"` (or `--no-batch`) sets that
|
|
106
|
+
loop for the task; the default is `batch.default` in `weight.json`.
|
|
95
107
|
`--files` lists every file of every step — the guard refuses an edit outside
|
|
96
108
|
them. `--tests` names the task's own tests; a bugfix names its failing test. At
|
|
97
109
|
guarded and full, run `start` only after the human accepted the plan.
|
|
@@ -30,7 +30,10 @@ STATE="python3 $AI_HOME/skills/ai-task/state.py"
|
|
|
30
30
|
(`review_rounds_max`): one full review and one scoped re-review. The pass past
|
|
31
31
|
it exits 6 `REVIEW_BUDGET` — the next verdict is the human's, from their own
|
|
32
32
|
terminal, with the command it prints, or the pick in the dialog (`DIALOG_PENDING`,
|
|
33
|
-
then the picked command verbatim, once). Never a third pass.
|
|
33
|
+
then the picked command verbatim, once). Never a third pass. A batch task
|
|
34
|
+
(`/rr-build-all`) has its own budget, `batch.review_rounds_max` full passes
|
|
35
|
+
(shipped three): after a `blockers_open` fix batch the next pass is a full one,
|
|
36
|
+
and the pass past the budget is the human's the same way.
|
|
34
37
|
|
|
35
38
|
## 2. Brief one `ai-reviewer`
|
|
36
39
|
|
|
@@ -49,14 +52,18 @@ the tier, so name the tier in the brief. Give it paths, not pasted text:
|
|
|
49
52
|
**Bounded inputs in light and guarded:** the goal, the diff, the named tests,
|
|
50
53
|
and the project's own threat model (`.ai/policies/safety.md`, the guards'
|
|
51
54
|
defaults, the test fixtures). A finding that needs anything outside them comes
|
|
52
|
-
back `[INFO]` with `in_scope: no`;
|
|
55
|
+
back `[INFO]` with `in_scope: no`; record it with `$STATE memory risk add`
|
|
53
56
|
as one entry — it never opens a fix. You may move a finding to
|
|
54
57
|
`in_scope: yes`, never the other way. **In full mode** the reviewer's own "look
|
|
55
|
-
for" list applies unbounded.
|
|
58
|
+
for" list applies unbounded. Tell it the cap: about 40 tool calls in light and
|
|
59
|
+
guarded, about 80 in full, then FINDINGS and VERDICT.
|
|
56
60
|
|
|
57
61
|
Before you delegate, probe the change yourself for a minute: the few inputs its
|
|
58
62
|
own threat model makes interesting, through the real code, and hand the
|
|
59
|
-
reviewer what you saw — it starts where you stopped.
|
|
63
|
+
reviewer what you saw — it starts where you stopped. When the change touches a
|
|
64
|
+
guard regex's running time, run the timed probe yourself (`/usr/bin/grep -E`,
|
|
65
|
+
or Python `timeit` on inputs of at most 4096 chars) and hand over the
|
|
66
|
+
measurement: the bounded reviewer may not run timing loops.
|
|
60
67
|
|
|
61
68
|
## 3. Record the verdict
|
|
62
69
|
|
|
@@ -77,12 +84,18 @@ task's record (`task.md`), so there is no separate report to write.
|
|
|
77
84
|
|
|
78
85
|
## 4. After the verdict
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
Continue from the verdict §3 recorded; do not record it again. Whatever the
|
|
88
|
+
verdict, each MEDIUM and LOW is one `$STATE memory risk add`
|
|
89
|
+
(`.ai/project/known-risks.md` amended into the task if missing), not a fix.
|
|
90
|
+
|
|
91
|
+
- **pass:** `/rr-ship`, i.e. `$STATE close` directly: the verdict is already
|
|
92
|
+
recorded. (To record and close in one call, §3's `review --verdict pass … <<'EOF' && $STATE close`;
|
|
93
|
+
`review` exits 0 for every verdict, so that chain is for `pass` only.)
|
|
94
|
+
- **pass_with_findings:** fix every in-scope HIGH in one batch, then
|
|
83
95
|
`$STATE verify --scope all --after-fix "H1,H2: <one line>"` and
|
|
84
96
|
`$STATE review --closes "H1,H2"`. The findings close on that green run, on a
|
|
85
|
-
tree the fix changed — no second review.
|
|
97
|
+
tree the fix changed — no second review. MEDIUM and LOW: `memory risk add`
|
|
98
|
+
as above; only a BLOCKER, a HIGH or a regression buys an `--after-fix` run.
|
|
86
99
|
- **blockers_open:** fix every BLOCKER and HIGH together, `verify --scope all
|
|
87
100
|
--after-fix "…"`, then **one scoped re-review** (`/rr-review B1,H1`): brief a fresh `ai-reviewer`
|
|
88
101
|
with the named findings and the fix's diff only — do they close, and what did
|
|
@@ -94,6 +107,8 @@ The `verify` after a fix is often the task's third whole-suite run: past
|
|
|
94
107
|
from their own terminal, with the command it prints, or the pick in the dialog.
|
|
95
108
|
|
|
96
109
|
A finding whose cause is the plan goes back to `/rr-plan` for that part only. A
|
|
97
|
-
real problem outside the task
|
|
110
|
+
real problem outside the task is recorded with `$STATE memory risk add`; a
|
|
111
|
+
fix in this task closes its risk with `$STATE memory risk close <rkey> --status
|
|
112
|
+
fixed --why "…"`. A mistake
|
|
98
113
|
this repository has seen before — the second time a review flags it — gets one
|
|
99
114
|
line in the project instruction file, in this task.
|
package/skills/rr-setup/SKILL.md
CHANGED
|
@@ -133,12 +133,25 @@ it cannot.
|
|
|
133
133
|
each in one plain sentence and ask, unless `$ARGUMENTS` holds `--apply`.
|
|
134
134
|
4. `$UPDATE --apply`. Exit 3 stopped on a changed, non-regular or unwritable
|
|
135
135
|
file and names it; nothing after it was written — run the dry run again.
|
|
136
|
-
**A `delete?` is the human's.** Never pass `--confirm-delete`, not even with
|
|
136
|
+
**A `delete?` is the human's.** Never pass `--confirm-delete` or `--confirm-cleanup`, not even with
|
|
137
137
|
`--apply`. Give them, with `$AI_HOME` and `$PWD` substituted, for their own
|
|
138
138
|
terminal (the `!` prefix is refused):
|
|
139
139
|
`python3 "<home>/skills/project-update/update.py" "<project>" --apply --confirm-delete "<your name>"`.
|
|
140
140
|
The schema stays put until it is settled — that command, or deleting the
|
|
141
|
-
file by hand; there is no third way.
|
|
141
|
+
file by hand; there is no third way. Two kinds are housekeeping, not
|
|
142
|
+
migrations: `[sdlc]` — an unedited `docs/sdlc/*/TEMPLATE.md`, which the plugin
|
|
143
|
+
now supplies (an edited one stays as the project's override); `[cleanup]` —
|
|
144
|
+
the `*.log` files of closed tasks, and the report and log files of an
|
|
145
|
+
abandoned task (its `state.json` stays) or of one unclosed for more than seven
|
|
146
|
+
days (never the task in flight; and none when `.ai/state/state.json` is unreadable).
|
|
147
|
+
`--confirm-delete` does not settle `[cleanup]` rows, and the rows differ by kind:
|
|
148
|
+
files under `.ai/state/logs/` are git-ignored and git cannot restore them; files
|
|
149
|
+
under `.ai/reports/` are usually tracked, and the row says "tracked: git can restore
|
|
150
|
+
a committed copy" or, when git holds no committed copy, "uncommitted" — then
|
|
151
|
+
commit `.ai/` first (the hint says so), and an `original/` copy is kept as for any
|
|
152
|
+
other deletion. They have their own command, the human's too, never passed by the agent:
|
|
153
|
+
`python3 "<home>/skills/project-update/update.py" "<project>" --apply --confirm-cleanup "<your name>"`.
|
|
154
|
+
Show both groups as printed. For 0006 (shared files) the order is:
|
|
142
155
|
`install.sh` for every runtime used, commit `.ai/`, read the `delete?` list,
|
|
143
156
|
then the command with no task in flight. Rollback is git:
|
|
144
157
|
`git restore --source=<commit before> --staged --worktree -- .ai`, before
|
package/skills/rr-setup/setup.py
CHANGED
|
@@ -182,7 +182,11 @@ def comment_purpose(lines):
|
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
def purpose_of(root, rel):
|
|
185
|
-
|
|
185
|
+
full = os.path.join(root, rel)
|
|
186
|
+
real, base = os.path.realpath(full), os.path.realpath(root)
|
|
187
|
+
if os.path.commonpath([real, base]) != base:
|
|
188
|
+
return None # a link out of the project: listed UNKNOWN, never read into toolbox.md
|
|
189
|
+
text = read_text(full)
|
|
186
190
|
if text is None:
|
|
187
191
|
return None
|
|
188
192
|
lines = text.splitlines()
|
|
@@ -225,6 +229,8 @@ def inventory(root, table):
|
|
|
225
229
|
for rel in paths:
|
|
226
230
|
if rel in seen or not pattern.match(rel):
|
|
227
231
|
continue
|
|
232
|
+
if "/hooks/" in rel and os.path.basename(rel).startswith("."):
|
|
233
|
+
continue
|
|
228
234
|
seen.add(rel)
|
|
229
235
|
if row["kind"] == "make":
|
|
230
236
|
for n, target, purpose in make_targets(root, rel):
|
package/skills/rr-ship/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rr-ship
|
|
3
|
-
description: Close the task — state.py close measures the two facts (a green whole-suite verify on the tree as it is now; every changed file inside the task's files), applies the mode table and either archives the task or, in full mode, requests the human's approval and prints the exact approve command for their own terminal. Writes the commit message body and suggests the commit; never commits, pushes or approves. Use on "/rr-ship", "close the task", "ship it", "ready for approval", after /rr-build or /rr-review.
|
|
3
|
+
description: Close the task — state.py close measures the two facts (a green whole-suite verify on the tree as it is now; every changed file inside the task's files), applies the mode table and either archives the task or, in full mode, requests the human's approval and prints the exact approve command for their own terminal. Writes the commit message body and suggests the commit; never merges or deploys, and commits, pushes or approves only as the command the human picked in the dialog, verbatim, once. Use on "/rr-ship", "close the task", "ship it", "ready for approval", after /rr-build or /rr-review.
|
|
4
4
|
argument-hint: [--abandon]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -33,7 +33,8 @@ Then the mode table decides:
|
|
|
33
33
|
|---|---|---|
|
|
34
34
|
| 0, `closed` | archived: `task.md` holds the changes and the verdict | §3 |
|
|
35
35
|
| 0, `ready for approval` | full mode: the gate is requested | §2 |
|
|
36
|
-
| 2 `review required`
|
|
36
|
+
| 2 `review required` whose text holds `only documents changed since run …` (read this row first) | a green suite, then documents only | `$STATE verify --scope step --files "<doc_tests>" && $STATE verify --scope e2e && $STATE close` — `close` prints the files comma-joined; use its text |
|
|
37
|
+
| 2 `review required`, any other reason | light with a red fact, or guarded/full without its review | `/rr-review` |
|
|
37
38
|
| 2 `verify required` | e2e owed, or not on this tree | `$STATE verify --scope all` (or `--scope e2e`, or `--e2e not-applicable "<why>"`) |
|
|
38
39
|
| 4 | a question is pending | show it; §4 |
|
|
39
40
|
| 5 `APPROVAL_REFUSED` | rejected, and nothing changed since | address the rejection, `verify --scope all`, close again |
|
|
@@ -54,9 +55,13 @@ Show the human the summary (§3), the open findings, the rollback, and that
|
|
|
54
55
|
command **verbatim** — paths absolute and quoted, because they paste it into
|
|
55
56
|
their own shell, where `$AI_HOME` is not set. Then **stop**.
|
|
56
57
|
|
|
57
|
-
- **You
|
|
58
|
-
`
|
|
59
|
-
|
|
58
|
+
- **You approve only through the dialog.** With `.ai/policies/dialog.json`
|
|
59
|
+
`enabled: true`, `close` offers the `approve` command in the runtime's dialog;
|
|
60
|
+
once the human picks it, run exactly that command, verbatim and once. With the
|
|
61
|
+
switch off, or no pick, you cannot approve: `approve` refuses without a terminal
|
|
62
|
+
on stdin or `AI_UNATTENDED` in its own environment — neither of which a tool
|
|
63
|
+
call from this session has — and the path guard denies it. Never try another
|
|
64
|
+
route.
|
|
60
65
|
- **full_irreversible:** the command carries
|
|
61
66
|
`--confirm "<what it does, naming DROP …>"`; the human names every operation
|
|
62
67
|
the re-score found, in their own words.
|
|
@@ -91,11 +96,14 @@ Write the body from `.ai/reports/<task-id>/task.md` — no new opinion:
|
|
|
91
96
|
- the verification command and its result (suite and e2e);
|
|
92
97
|
- the review verdict, with any finding left open and why;
|
|
93
98
|
- the rollback: `git revert <commit>`, plus anything a revert does not undo;
|
|
94
|
-
- open risks
|
|
99
|
+
- open risks: the ones this task recorded or closed with `state.py memory risk
|
|
100
|
+
add|close`, by rkey (`state.py memory risks` lists them).
|
|
95
101
|
|
|
96
102
|
Suggest the commit command — `git add` of the task's files and its
|
|
97
|
-
`.ai/reports/<task-id
|
|
98
|
-
|
|
103
|
+
`.ai/reports/<task-id>/` (in packages mode also `.ai/memory/`, which holds the
|
|
104
|
+
task's record — the pre-push check refuses a closed task without it), then
|
|
105
|
+
`git commit` with that body — for the human to run. You never merge or deploy; you commit, push or
|
|
106
|
+
approve only as the command the human picked in the dialog (§2), verbatim, once.
|
|
99
107
|
|
|
100
108
|
## 4. Pending questions
|
|
101
109
|
|
package/skills/rr-spec/SKILL.md
CHANGED
|
@@ -19,7 +19,9 @@ STATE="python3 $AI_HOME/skills/ai-task/state.py"
|
|
|
19
19
|
|
|
20
20
|
## 1. Prerequisites
|
|
21
21
|
|
|
22
|
-
`docs/sdlc/
|
|
22
|
+
`docs/sdlc/constitution.md` exists, else run `/rr-setup` first. The template is
|
|
23
|
+
`docs/sdlc/specs/TEMPLATE.md` when the project overrides it, else
|
|
24
|
+
`$AI_HOME/skills/project-init/templates/spec.md`. Derive
|
|
23
25
|
`<slug>` from the topic (kebab-case, at most five words). If
|
|
24
26
|
`docs/sdlc/specs/<slug>.md` exists, offer to refine it instead of starting over.
|
|
25
27
|
When `$ARGUMENTS` is a path to an intent file, read it fully — it is small by
|
|
@@ -75,7 +77,7 @@ decision — moves this to the top tier.
|
|
|
75
77
|
|
|
76
78
|
## 5. Write
|
|
77
79
|
|
|
78
|
-
Copy
|
|
80
|
+
Copy the template (§1) to `docs/sdlc/specs/<slug>.md`, creating the folder, replace
|
|
79
81
|
`{{TITLE}}` and `{{SLUG}}`. Replace the template's `Intent:` line with:
|
|
80
82
|
|
|
81
83
|
```markdown
|
|
@@ -24,8 +24,8 @@ After the output, at most one line, and only when a line asks for it:
|
|
|
24
24
|
- `questions: N pending` with N > 0: the task is halted until they are
|
|
25
25
|
answered — `/rr-build` shows them. The count can lag an `[Answer]:` line the
|
|
26
26
|
human filled in the file; `state.py handoff --print` re-reads the file.
|
|
27
|
-
- `gate: requested`: the
|
|
28
|
-
|
|
27
|
+
- `gate: requested`: the `approve` command `/rr-ship` printed is run by the human in their
|
|
28
|
+
own terminal, or by you only if they picked it in the dialog (verbatim, once).
|
|
29
29
|
|
|
30
30
|
Exit 1 with `root: none` means no `.ai/` governs this directory: `/rr-setup`
|
|
31
31
|
sets one up.
|
|
@@ -637,27 +637,61 @@ def find_project(start: str) -> str | None:
|
|
|
637
637
|
d = parent
|
|
638
638
|
|
|
639
639
|
|
|
640
|
+
def seal_events(project: str, task: str) -> list:
|
|
641
|
+
"""R20 (compact-project-memory): after cleanup only the record knows the
|
|
642
|
+
task — its seal's started_at, closed_at, mode and runtimes stand in for
|
|
643
|
+
the journal's first and last lines."""
|
|
644
|
+
base = os.path.join(project, ".ai", "memory")
|
|
645
|
+
seal = None
|
|
646
|
+
for name in sorted(os.listdir(base)) if os.path.isdir(base) else []:
|
|
647
|
+
if not name.endswith(".jsonl"):
|
|
648
|
+
continue
|
|
649
|
+
with open(os.path.join(base, name), encoding="utf-8", errors="replace") as fh:
|
|
650
|
+
for line in fh:
|
|
651
|
+
rec = _record(line)
|
|
652
|
+
if rec and rec.get("kind") == "task" and rec.get("task") == task:
|
|
653
|
+
if seal is None or (rec.get("inst") or 0) >= (seal.get("inst") or 0):
|
|
654
|
+
seal = rec
|
|
655
|
+
if not seal or not seal.get("started_at"):
|
|
656
|
+
return []
|
|
657
|
+
runtimes = [rt for rt in seal.get("runtimes") or [] if rt in RUNTIMES]
|
|
658
|
+
out = [{"ts": seal["started_at"], "event": "task_started", "runtime": rt} for rt in runtimes or [None]]
|
|
659
|
+
out.append({"ts": seal["started_at"], "event": "tier_set",
|
|
660
|
+
"data": {"tier": seal.get("legacy_tier") or "untiered", "mode": seal.get("mode")}})
|
|
661
|
+
if seal.get("closed_at"):
|
|
662
|
+
out.append({"ts": seal["closed_at"], "event": "task_closed"})
|
|
663
|
+
return out
|
|
664
|
+
|
|
665
|
+
|
|
640
666
|
def task_report(args) -> int:
|
|
641
667
|
"""One task's window from its journal, the tokens each runtime spent in it,
|
|
642
668
|
and each involved runtime's budget for the task's final tier."""
|
|
643
669
|
project = find_project(args.project or os.getcwd())
|
|
644
670
|
journal = os.path.join(project or "", ".ai", "reports", args.task, "events.jsonl")
|
|
645
|
-
|
|
646
|
-
print(f"no journal for {args.task} (looked for {journal})")
|
|
647
|
-
return 1
|
|
671
|
+
moved = os.path.join(project or "", ".ai", "state", "logs", args.task, "report", "events.jsonl")
|
|
648
672
|
events = []
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
673
|
+
if project and not os.path.isfile(journal) and os.path.isfile(moved):
|
|
674
|
+
journal = moved # cleanup moved the report directory into the local raw store
|
|
675
|
+
if not project or not os.path.isfile(journal):
|
|
676
|
+
events = seal_events(project, args.task) if project else []
|
|
677
|
+
if not events:
|
|
678
|
+
print(f"no journal for {args.task} (looked for {journal})")
|
|
679
|
+
return 1
|
|
680
|
+
print(f"(no journal: the window is the record's seal for {args.task})")
|
|
681
|
+
else:
|
|
682
|
+
with open(journal, encoding="utf-8", errors="replace") as fh:
|
|
683
|
+
for line in fh:
|
|
684
|
+
rec = _record(line)
|
|
685
|
+
if rec is not None:
|
|
686
|
+
events.append(rec)
|
|
654
687
|
stamps = [parse_ts(e.get("ts")) for e in events if parse_ts(e.get("ts"))]
|
|
655
688
|
if not stamps:
|
|
656
689
|
print(f"the journal of {args.task} has no timestamps")
|
|
657
690
|
return 1
|
|
658
691
|
start = min(stamps)
|
|
659
692
|
closed = [parse_ts(e.get("ts")) for e in events if e.get("event") == "task_closed"]
|
|
660
|
-
|
|
693
|
+
unclosed = not any(closed)
|
|
694
|
+
end = max(stamps) if unclosed else max(c for c in closed if c) # unclosed: the last event, not now
|
|
661
695
|
tier, mode = "untiered", None
|
|
662
696
|
involved = []
|
|
663
697
|
for e in events:
|
|
@@ -696,7 +730,8 @@ def task_report(args) -> int:
|
|
|
696
730
|
|
|
697
731
|
label = f"mode {mode} (tier {tier})" if mode else f"tier {tier}"
|
|
698
732
|
print(f"task {args.task} {label} window {start.isoformat(timespec='seconds')}"
|
|
699
|
-
f"..{end.isoformat(timespec='seconds')}"
|
|
733
|
+
f"..{end.isoformat(timespec='seconds')}"
|
|
734
|
+
+ (" (unclosed: window ends at the last event)" if unclosed else ""))
|
|
700
735
|
print(" (by time window: any other session in this project inside it is counted too)")
|
|
701
736
|
for rt in involved or sorted(used):
|
|
702
737
|
inp, cache, out = used.get(rt, [0, 0, 0])
|