riskrails 3.0.1 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +29 -15
- 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 +27 -30
- package/bin/riskrails.js +114 -23
- package/hooks/ai-path-guard-defaults.json +3 -2
- package/hooks/context-guard.py +15 -12
- package/hooks/dialog-gate.py +1 -1
- package/hooks/guard/common.py +2 -0
- package/hooks/guard/dialog.py +107 -27
- package/hooks/guard/git.py +12 -3
- package/hooks/guard/paths.py +150 -37
- package/hooks/project-scaffold.py +3 -6
- package/hooks/runtime-gate.py +134 -33
- package/hooks/update_notice.py +22 -16
- package/instructions/routing.md +1 -1
- package/instructions/stub.md +3 -3
- package/package.json +1 -1
- package/skills/AGENTS.md +1 -1
- package/skills/CLAUDE.md +1 -1
- package/skills/ai-init/templates/.ai/AGENTS.md +1 -1
- package/skills/ai-init/templates/.ai/VERSION +1 -1
- 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/state/README.md +4 -4
- 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/rr_state/__init__.py +1 -0
- package/skills/ai-task/rr_state/cli.py +210 -0
- package/skills/ai-task/rr_state/close.py +171 -0
- package/skills/ai-task/rr_state/dialog.py +541 -0
- package/skills/ai-task/rr_state/dialog_cmd.py +164 -0
- package/skills/ai-task/rr_state/fs.py +220 -0
- package/skills/ai-task/rr_state/gate.py +125 -0
- package/skills/ai-task/rr_state/gate_cmd.py +128 -0
- package/skills/ai-task/rr_state/handoff.py +226 -0
- package/skills/ai-task/rr_state/journal.py +126 -0
- package/skills/ai-task/rr_state/labels.py +99 -0
- package/skills/ai-task/rr_state/ledger.py +145 -0
- package/skills/ai-task/rr_state/locks.py +84 -0
- package/skills/ai-task/rr_state/memory_cmd.py +310 -0
- package/skills/ai-task/rr_state/policy.py +378 -0
- package/skills/ai-task/rr_state/profile.py +373 -0
- package/skills/ai-task/rr_state/questions.py +336 -0
- package/skills/ai-task/rr_state/questions_cmd.py +321 -0
- package/skills/ai-task/rr_state/report.py +271 -0
- package/skills/ai-task/rr_state/report_cmd.py +230 -0
- package/skills/ai-task/rr_state/review.py +165 -0
- package/skills/ai-task/rr_state/runtime.py +80 -0
- package/skills/ai-task/rr_state/start.py +257 -0
- package/skills/ai-task/rr_state/steps.py +131 -0
- package/skills/ai-task/rr_state/task.py +396 -0
- package/skills/ai-task/rr_state/verify.py +231 -0
- package/skills/ai-task/sensors.py +37 -4
- package/skills/ai-task/state.py +24 -5138
- package/skills/project-init/templates/constitution.md +1 -1
- package/skills/project-init/templates/sdlc-README.md +20 -14
- package/skills/project-update/adopt.py +38 -24
- 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/18c7a2753ce9cb6f6d3d1e9d5d1125d75950b879d56b7325225b200869631b29 +15 -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/5a93f78b4ed4d416c171139b2ce610a117f9c1dd46fbb56ca86729cb4c53d10c +6 -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/7f063e79195a5c85c37b7260dd20c184d3b9bea85e37202e0bbca73ad84bba49 +13 -0
- package/skills/project-update/history/blobs/a51517e13e57420d2d88a6bcfb4055d762d4a0d10a8f88450929c5c4572213a9 +67 -0
- package/skills/project-update/history/blobs/c8573ca1f810ce6c4ac49456a4c1725b93e0d3b74c54ce95d46d8747777895f0 +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/index.json +38 -16
- package/skills/project-update/migrations/0003_context_diet.py +1 -1
- package/skills/project-update/migrations/0007_weight_model.py +130 -23
- package/skills/project-update/migrations/0011_retire_risk_tiers.py +37 -0
- package/skills/project-update/migrations/README.md +16 -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 +569 -106
- package/skills/rr-build-all/SKILL.md +2 -2
- package/skills/rr-plan/SKILL.md +3 -1
- package/skills/rr-review/SKILL.md +13 -8
- package/skills/rr-setup/SKILL.md +24 -4
- package/skills/rr-setup/setup.py +7 -1
- package/skills/rr-spec/SKILL.md +4 -2
- package/skills/rr-status/status.py +36 -9
- package/skills/usage-report/usage-report.py +4 -2
- package/skills/ai-init/templates/.ai/policies/risk-tiers.json +0 -628
- package/skills/ai-init/templates/.ai/policies/risk-tiers.md +0 -180
|
@@ -146,8 +146,8 @@ EOF
|
|
|
146
146
|
- **pass** → §6.
|
|
147
147
|
- **pass_with_findings** → the in-scope HIGHs, grouped by file, one fixer per group in
|
|
148
148
|
parallel; `$STATE verify --scope all --after-fix "H1,H2: …"`; `$STATE review --closes "H1,H2"`
|
|
149
|
-
→ §6.
|
|
150
|
-
or a regression buys an `--after-fix` run.
|
|
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
151
|
- **blockers_open** → every in-scope BLOCKER and HIGH, grouped by file, one fixer per group in
|
|
152
152
|
parallel; `$STATE verify --scope all --after-fix "B1,H1: …"` (red: §4's batch); then
|
|
153
153
|
**§5 again, a full pass** — a fix batch across many files is a new change, not a scoped one.
|
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
|
|
@@ -60,7 +60,10 @@ guarded, about 80 in full, then FINDINGS and VERDICT.
|
|
|
60
60
|
|
|
61
61
|
Before you delegate, probe the change yourself for a minute: the few inputs its
|
|
62
62
|
own threat model makes interesting, through the real code, and hand the
|
|
63
|
-
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.
|
|
64
67
|
|
|
65
68
|
## 3. Record the verdict
|
|
66
69
|
|
|
@@ -81,16 +84,18 @@ task's record (`task.md`), so there is no separate report to write.
|
|
|
81
84
|
|
|
82
85
|
## 4. After the verdict
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.)
|
|
87
94
|
- **pass_with_findings:** fix every in-scope HIGH in one batch, then
|
|
88
95
|
`$STATE verify --scope all --after-fix "H1,H2: <one line>"` and
|
|
89
96
|
`$STATE review --closes "H1,H2"`. The findings close on that green run, on a
|
|
90
|
-
tree the fix changed — no second review.
|
|
91
|
-
|
|
92
|
-
if missing), not a fix; only a BLOCKER, a HIGH or a regression buys an
|
|
93
|
-
`--after-fix` run.
|
|
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.
|
|
94
99
|
- **blockers_open:** fix every BLOCKER and HIGH together, `verify --scope all
|
|
95
100
|
--after-fix "…"`, then **one scoped re-review** (`/rr-review B1,H1`): brief a fresh `ai-reviewer`
|
|
96
101
|
with the named findings and the fix's diff only — do they close, and what did
|
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
|
|
@@ -157,7 +170,12 @@ it cannot.
|
|
|
157
170
|
`that rule is not rendered`: name the `.ai/rules/` file and what it lacks
|
|
158
171
|
(`paths:` and a body). `overrides the plugin's shared copy`: offer to diff
|
|
159
172
|
each and delete those that add nothing. `foreign structure detected`: offer
|
|
160
|
-
**--adopt**.
|
|
173
|
+
**--adopt**. `tracked by git but ignored`: show the line as printed and give
|
|
174
|
+
the human the command it prints, exactly as printed, plus a commit, for their
|
|
175
|
+
own terminal (never rebuilt from the path; only the xargs form untracks every
|
|
176
|
+
such file under `.ai/state`, a printed `rm --cached -- <path>` one file; a
|
|
177
|
+
second machine runs the same before its next pull, else a modify/delete
|
|
178
|
+
conflict); never run it yourself.
|
|
161
179
|
7. `$UPDATE --check` and `git diff --stat`; say what changed, what you merged
|
|
162
180
|
and what is the human's. Suggest `git add -A .ai docs/sdlc .gitignore` plus
|
|
163
181
|
the runtime directories and instruction files.
|
|
@@ -177,7 +195,9 @@ overwrite. A foreign tool's structure, below, can be present as well.
|
|
|
177
195
|
|
|
178
196
|
Every command is `$UPDATE --adopt …`, with `--mode coexist` on each when the team
|
|
179
197
|
still uses the other tool (router rows only, nothing moves). Exit 5
|
|
180
|
-
`ADOPT_REFUSED`: show the first line and stop. Exit
|
|
198
|
+
`ADOPT_REFUSED`: show the first line and stop. Exit 2: the routing table or a
|
|
199
|
+
migration could not be read (reason on stderr, prefixed `project-update:`),
|
|
200
|
+
nothing written. Exit 4 `ADOPT_INCOMPLETE`:
|
|
181
201
|
`unmapped` files and a `split?` are yours to settle, then dry-run again; a
|
|
182
202
|
`conflict` or a check `FAIL` left after that is the human's — never edit a
|
|
183
203
|
destination or a source to make it pass.
|
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-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
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
Read-only and model-free. A schema-7 task is described by `state.py status`;
|
|
7
7
|
for any other task, and whenever that call fails, it reads the files directly —
|
|
8
|
-
.ai/state/state.json (or the legacy current.json), the task's events.jsonl, .ai/policies/
|
|
8
|
+
.ai/state/state.json (or the legacy current.json), the task's events.jsonl, .ai/policies/weight.json (else the shipped table)
|
|
9
9
|
and .ai/VERSION — so it answers in a project whose state.py is older, newer or
|
|
10
10
|
broken. The root is found the way the guards find it (find_ai_root in
|
|
11
11
|
hooks/lib/ai-hook-common.sh): the nearest ancestor holding .ai/, not stopping at
|
|
@@ -81,7 +81,7 @@ def schema_line(root):
|
|
|
81
81
|
numbers = [int(name[:4]) for name in names if re.match(r"\d{4}_", name)]
|
|
82
82
|
plugin = str(max(numbers)) if numbers else "?"
|
|
83
83
|
behind = project.isdigit() and plugin.isdigit() and int(project) < int(plugin)
|
|
84
|
-
return "schema: project %s · plugin %s%s" % (project, plugin, " — behind: run /
|
|
84
|
+
return "schema: project %s · plugin %s%s" % (project, plugin, " — behind: run /rr-setup --update" if behind else "")
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def one_line(value, limit=160):
|
|
@@ -97,13 +97,40 @@ def short(items, limit=4):
|
|
|
97
97
|
return ", ".join(items[:limit]) + more if items else "none"
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
def budgets(root, state):
|
|
101
|
+
"""(max_suite_runs, review_rounds_max) as state.py's budget() enforces them:
|
|
102
|
+
weight.json's batch section while the batch loop is on, else tests.max_suite_runs
|
|
103
|
+
and review_rounds_max. A project without weight.json runs the shipped table."""
|
|
104
|
+
policy = read_json(os.path.join(root, ".ai", "policies", "weight.json")) \
|
|
105
|
+
or read_json(os.path.join(SKILLS, "ai-init", "templates", ".ai", "policies", "weight.json"))
|
|
106
|
+
|
|
107
|
+
def whole(value, default):
|
|
108
|
+
return value if isinstance(value, int) and not isinstance(value, bool) and value >= 1 else default
|
|
109
|
+
|
|
110
|
+
if (state.get("batch") or {}).get("on"):
|
|
111
|
+
section = policy.get("batch") if isinstance(policy.get("batch"), dict) else {}
|
|
112
|
+
return whole(section.get("max_suite_runs"), 8), whole(section.get("review_rounds_max"), 3)
|
|
113
|
+
tests = policy.get("tests") if isinstance(policy.get("tests"), dict) else {}
|
|
114
|
+
return tests.get("max_suite_runs", 2), policy.get("review_rounds_max", 2)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def counter(state, section, key, legacy):
|
|
118
|
+
"""A schema-7 counter (verify.suite_runs, review.passes), else the legacy one
|
|
119
|
+
(tests.suite_runs, reviews); 0 when neither is a number."""
|
|
120
|
+
new = state.get(section)
|
|
121
|
+
if isinstance(new, dict) and isinstance(new.get(key), int):
|
|
122
|
+
return new[key]
|
|
123
|
+
old = state.get(legacy)
|
|
124
|
+
old = old.get(key) if isinstance(old, dict) else old
|
|
125
|
+
return old if isinstance(old, int) else 0
|
|
126
|
+
|
|
127
|
+
|
|
100
128
|
def report(root):
|
|
101
129
|
lines = ["root: %s" % root]
|
|
102
130
|
state = read_json(state_file(root))
|
|
103
131
|
if not state.get("task_id"):
|
|
104
132
|
return lines + ["task: none — no task in flight", schema_line(root)], None
|
|
105
|
-
|
|
106
|
-
tests_cfg = ((policy.get("sensors") or {}).get("tests") or {})
|
|
133
|
+
max_runs, max_rounds = budgets(root, state)
|
|
107
134
|
plan = state.get("approved_plan") or {}
|
|
108
135
|
steps = plan.get("steps") or []
|
|
109
136
|
current = plan.get("current_step_id")
|
|
@@ -122,11 +149,11 @@ def report(root):
|
|
|
122
149
|
else "step: none current (%d of %d done)" % (done, len(steps))),
|
|
123
150
|
"verify: tests %s · suite runs %s/%s · e2e %s"
|
|
124
151
|
% (state.get("test_status") or "?",
|
|
125
|
-
(state
|
|
126
|
-
|
|
152
|
+
counter(state, "verify", "suite_runs", "tests"),
|
|
153
|
+
max_runs, state.get("e2e_status") or "?"),
|
|
127
154
|
"review: %s (%s of %s) · security %s"
|
|
128
|
-
% (state.get("review_status") or "?", state
|
|
129
|
-
|
|
155
|
+
% (state.get("review_status") or "?", counter(state, "review", "passes", "reviews"),
|
|
156
|
+
max_rounds, state.get("security_status") or "?"),
|
|
130
157
|
"approval: %s" % ("granted by %s" % approval.get("granted_by") if approval.get("granted")
|
|
131
158
|
else "requested, waiting for the human" if approval.get("requested_at")
|
|
132
159
|
else "not requested"),
|
|
@@ -146,7 +173,7 @@ def main(argv=None):
|
|
|
146
173
|
args = parser.parse_args(argv)
|
|
147
174
|
root = find_ai_root(args.root)
|
|
148
175
|
if not root:
|
|
149
|
-
print("root: none — no .ai/ in %s or above it; /
|
|
176
|
+
print("root: none — no .ai/ in %s or above it; run /rr-setup first" % os.path.abspath(args.root))
|
|
150
177
|
return 1
|
|
151
178
|
lines, task_id = None, None
|
|
152
179
|
state = read_json(state_file(root))
|
|
@@ -690,7 +690,8 @@ def task_report(args) -> int:
|
|
|
690
690
|
return 1
|
|
691
691
|
start = min(stamps)
|
|
692
692
|
closed = [parse_ts(e.get("ts")) for e in events if e.get("event") == "task_closed"]
|
|
693
|
-
|
|
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
|
|
694
695
|
tier, mode = "untiered", None
|
|
695
696
|
involved = []
|
|
696
697
|
for e in events:
|
|
@@ -729,7 +730,8 @@ def task_report(args) -> int:
|
|
|
729
730
|
|
|
730
731
|
label = f"mode {mode} (tier {tier})" if mode else f"tier {tier}"
|
|
731
732
|
print(f"task {args.task} {label} window {start.isoformat(timespec='seconds')}"
|
|
732
|
-
f"..{end.isoformat(timespec='seconds')}"
|
|
733
|
+
f"..{end.isoformat(timespec='seconds')}"
|
|
734
|
+
+ (" (unclosed: window ends at the last event)" if unclosed else ""))
|
|
733
735
|
print(" (by time window: any other session in this project inside it is counted too)")
|
|
734
736
|
for rt in involved or sorted(used):
|
|
735
737
|
inp, cache, out = used.get(rt, [0, 0, 0])
|