pirl-framework-cli 0.0.0-stage → 1.0.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/CHANGELOG.md +50 -0
- package/LICENSE +21 -0
- package/README.md +265 -2
- package/bundled-framework/files/.agents/evals/layer_record.py +438 -0
- package/bundled-framework/files/.agents/evals/tests/test_layer_record.py +264 -0
- package/bundled-framework/files/.agents/evals/tests/test_skills.py +210 -0
- package/bundled-framework/files/.agents/evals/tests/test_validate_layer_artifact.py +214 -0
- package/bundled-framework/files/.agents/evals/validate_layer_artifact.py +415 -0
- package/bundled-framework/files/.agents/instructions/implement.instructions.md +343 -0
- package/bundled-framework/files/.agents/instructions/layered-task-execution.instructions.md +370 -0
- package/bundled-framework/files/.agents/instructions/layout-verification.instructions.md +107 -0
- package/bundled-framework/files/.agents/instructions/plan.instructions.md +292 -0
- package/bundled-framework/files/.agents/instructions/review.instructions.md +236 -0
- package/bundled-framework/files/.agents/instructions/self-evaluation.instructions.md +146 -0
- package/bundled-framework/files/.agents/instructions/standalone-and-maintenance.instructions.md +230 -0
- package/bundled-framework/files/.agents/roles/backend-developer.role.md +45 -0
- package/bundled-framework/files/.agents/roles/business-analyst.role.md +55 -0
- package/bundled-framework/files/.agents/roles/code-analyst.role.md +63 -0
- package/bundled-framework/files/.agents/roles/code-reviewer.role.md +46 -0
- package/bundled-framework/files/.agents/roles/frontend-developer.role.md +51 -0
- package/bundled-framework/files/.agents/roles/process-auditor.role.md +62 -0
- package/bundled-framework/files/.agents/roles/solution-architect.role.md +53 -0
- package/bundled-framework/files/.agents/roles/technical-writer.role.md +68 -0
- package/bundled-framework/files/.agents/roles/ux-designer.role.md +46 -0
- package/bundled-framework/files/.skills/pirl-implement/SKILL.md +115 -0
- package/bundled-framework/files/.skills/pirl-implement/assets/implementation-record-template.md +22 -0
- package/bundled-framework/files/.skills/pirl-implement/references/implementation-record-format.md +41 -0
- package/bundled-framework/files/.skills/pirl-implement/scripts/add_implementation_record.py +58 -0
- package/bundled-framework/files/.skills/pirl-implement/scripts/new_layout_run.py +51 -0
- package/bundled-framework/files/.skills/pirl-plan/SKILL.md +100 -0
- package/bundled-framework/files/.skills/pirl-plan/assets/plan-template.md +48 -0
- package/bundled-framework/files/.skills/pirl-plan/references/plan-format.md +65 -0
- package/bundled-framework/files/.skills/pirl-plan/scripts/new_plan.py +93 -0
- package/bundled-framework/files/.skills/pirl-review/SKILL.md +106 -0
- package/bundled-framework/files/.skills/pirl-review/assets/review-template.md +92 -0
- package/bundled-framework/files/.skills/pirl-review/references/review-format.md +70 -0
- package/bundled-framework/files/.skills/pirl-review/scripts/new_review.py +83 -0
- package/bundled-framework/files/AGENTS.md +303 -0
- package/bundled-framework/files/CONTEXT.md +187 -0
- package/bundled-framework/files/LICENSE +21 -0
- package/bundled-framework/files/README.md +170 -0
- package/bundled-framework/files/gitignore.pirl-bundled +12 -0
- package/bundled-framework/manifest.json +45 -0
- package/dist/bin/main.js +128 -0
- package/dist/bin/pirl.js +6 -0
- package/dist/commands/dispatch.js +57 -0
- package/dist/commands/registry.js +39 -0
- package/dist/core/clipboard.js +70 -0
- package/dist/core/config.js +73 -0
- package/dist/core/exec.js +32 -0
- package/dist/core/paths.js +34 -0
- package/dist/core/targetRepo.js +22 -0
- package/dist/doctor/doctor.js +95 -0
- package/dist/engine/agentMerge.js +71 -0
- package/dist/engine/agents.js +67 -0
- package/dist/engine/attachments.js +251 -0
- package/dist/engine/claudeQuota.js +62 -0
- package/dist/engine/claudeRunner.js +118 -0
- package/dist/engine/claudeStream.js +205 -0
- package/dist/engine/hubData.js +176 -0
- package/dist/engine/permissions.js +67 -0
- package/dist/engine/pricing.js +24 -0
- package/dist/engine/protocol.js +98 -0
- package/dist/engine/run.js +527 -0
- package/dist/engine/runStore.js +67 -0
- package/dist/engine/types.js +3 -0
- package/dist/hub/framework.js +13 -0
- package/dist/hub/hub.js +125 -0
- package/dist/hub/services.js +38 -0
- package/dist/hub/update.js +261 -0
- package/dist/providers/claude.js +62 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/types.js +1 -0
- package/dist/ui/App.js +849 -0
- package/dist/ui/browse.js +144 -0
- package/dist/ui/components/Header.js +54 -0
- package/dist/ui/components/Input.js +76 -0
- package/dist/ui/components/Lines.js +50 -0
- package/dist/ui/components/Panels.js +73 -0
- package/dist/ui/content.js +225 -0
- package/dist/ui/frameworkConsole.js +541 -0
- package/dist/ui/inputEditor.js +86 -0
- package/dist/ui/layout.js +36 -0
- package/dist/ui/lines.js +15 -0
- package/dist/ui/listOverlay.js +97 -0
- package/dist/ui/runView.js +305 -0
- package/dist/ui/session.js +1 -0
- package/dist/ui/terminalInput.js +45 -0
- package/dist/ui/text.js +58 -0
- package/dist/ui/theme.js +139 -0
- package/dist/ui/ticker.js +48 -0
- package/package.json +67 -4
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: pirl-plan/v1
|
|
3
|
+
content_language: en
|
|
4
|
+
plan_set: {{plan_set}}
|
|
5
|
+
task_id: {{task_id}}
|
|
6
|
+
title: "<Task name>"
|
|
7
|
+
layer: implement
|
|
8
|
+
status: planned
|
|
9
|
+
depends_on: []
|
|
10
|
+
created_at: "{{created_at}}"
|
|
11
|
+
target_repo: "<Path of the target repository, relative to the Hub root or absolute; . for the Hub itself>"
|
|
12
|
+
source_request: "<Short version of the user's request>"
|
|
13
|
+
planning_roles: [<planning-role>]
|
|
14
|
+
implementation_roles: [<implementation-role>]
|
|
15
|
+
verification_roles: [<verification-role>]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# {{task_id}}: <Task name>
|
|
19
|
+
|
|
20
|
+
## What to do
|
|
21
|
+
|
|
22
|
+
- REQ-01 | Source: <Request or file/symbol read> | Requirement: <Business behavior to achieve>
|
|
23
|
+
- Scope: <Work to be done; write exclusions only when needed to avoid misunderstanding>.
|
|
24
|
+
- Allowed paths: `<specific paths of source, documentation and tests under FUT, if any>`.
|
|
25
|
+
- Standalone task outcome: <Output that can be accepted while later/parallel tasks are not yet implemented>.
|
|
26
|
+
<If the specification has build/test/release goals: one line saying they belong to the user's build/test/release stage>.
|
|
27
|
+
|
|
28
|
+
## How to do it
|
|
29
|
+
|
|
30
|
+
1. <ROLE-01: which file/symbol to change, how and with what outcome; add steps when there is a real dependency>.
|
|
31
|
+
<If there are tests: ROLE-01 writes/updates tests under FUT for which REQ and which affected functionality, and runs exactly those tests (file/class/name)>.
|
|
32
|
+
2. <ROLE-02: after ROLE-01, reasons on the diff/source to conclude which QGs; runs no commands/tests>.
|
|
33
|
+
|
|
34
|
+
<If depends_on is set: the concrete output to receive from each prerequisite task>.
|
|
35
|
+
<If end-to-end criteria are assigned to another task: the owning task and the integration part to verify; not a QG of this task>.
|
|
36
|
+
<Input conditions or decisions to stop at, if any>.
|
|
37
|
+
Next step: the user names this Plan file for Implement once the Plan content
|
|
38
|
+
fixes are complete; Implement carries out only this task within the scope above.
|
|
39
|
+
|
|
40
|
+
## Participating roles
|
|
41
|
+
|
|
42
|
+
- ROLE-01 | Type: implementation | Role: <implementation-role> | Responsible for: <Concrete work> | Input: <Information or dependent results> | Done when: <Source/documentation to deliver>
|
|
43
|
+
- ROLE-02 | Type: verification | Role: <verification-role> | Responsible for: <QGs and the questions to answer> | Input: <ROLE-01's diff/source> | Done when: <PASS/FAIL/BLOCKED conclusion for each QG by reasoning with sources>
|
|
44
|
+
- Planning | Roles: <the roles in planning_roles> | Execution: <separate agent, or the coordinator self-performing with the reason delegation was not possible; per role when they differ>
|
|
45
|
+
|
|
46
|
+
## Required quality gates
|
|
47
|
+
|
|
48
|
+
- QG-01 | Requirement: REQ-01 | Required logic: <Situation → where it is handled → outcome/state and failure branches; provable by reasoning on the source with the prerequisite inputs and this task's changes, without tests/builds/runs or later/parallel tasks>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Plan file format
|
|
2
|
+
|
|
3
|
+
The `pirl-plan` skill owns how a Plan file is laid out, named and stored. What each
|
|
4
|
+
section must settle belongs to the
|
|
5
|
+
[Plan contract](../../../.agents/instructions/plan.instructions.md).
|
|
6
|
+
|
|
7
|
+
## Location and naming
|
|
8
|
+
|
|
9
|
+
- Output directory: the directory the user names for the Plan; when the user
|
|
10
|
+
names none, the Hub root.
|
|
11
|
+
- Package: `<output directory>/<request-slug>_<YYYYMMDD-HHmmss>/`. The
|
|
12
|
+
package directory name is the `plan_set`.
|
|
13
|
+
- Task file: `<NN>-plan-<todo-slug>-<YYYYMMDD-HHmmss>.md`, where `NN` is the
|
|
14
|
+
task number (`T01` → `01`) and the timestamp is the package timestamp, shared
|
|
15
|
+
by every file of the package.
|
|
16
|
+
- Slugs are ASCII kebab-case: lowercase letters, digits and single hyphens.
|
|
17
|
+
- Timestamps use the machine's local time zone and carry its UTC offset, for
|
|
18
|
+
example `+07:00`.
|
|
19
|
+
- Task IDs start at `T01` and are consecutive within the package.
|
|
20
|
+
- Never overwrite an existing file. Review reports of the package are placed
|
|
21
|
+
per the [report format](../../pirl-review/references/review-format.md#location-and-naming).
|
|
22
|
+
- Git ignores package directories anywhere in the Hub, by their name pattern
|
|
23
|
+
([AGENTS.md](../../../AGENTS.md#git)); a package the user places outside the
|
|
24
|
+
Hub follows the Git rules of that location.
|
|
25
|
+
|
|
26
|
+
## Frontmatter
|
|
27
|
+
|
|
28
|
+
| Field | Value |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `schema` | `pirl-plan/v1` |
|
|
31
|
+
| `content_language` | `en` |
|
|
32
|
+
| `plan_set` | The package directory name |
|
|
33
|
+
| `task_id` | `T01`, `T02`, … |
|
|
34
|
+
| `title` | Task name, quoted |
|
|
35
|
+
| `layer` | `implement`: the layer that carries out the Plan |
|
|
36
|
+
| `status` | `planned` at handoff; Implement later sets `awaiting_confirmation`, `implemented`, `blocked` or `failed`, equal to the Implementation Record's `status` |
|
|
37
|
+
| `depends_on` | Task IDs of the same package; never the task itself, never a cycle |
|
|
38
|
+
| `created_at` | ISO-8601 timestamp with a UTC offset, quoted |
|
|
39
|
+
| `target_repo` | Path of the target repository, relative to the Hub root or absolute; `.` for the Hub itself; an existing directory ([AGENTS.md](../../../AGENTS.md#target-repository)) |
|
|
40
|
+
| `source_request` | Short version of the user's request, quoted |
|
|
41
|
+
| `planning_roles` | Roles registered for `plan` |
|
|
42
|
+
| `implementation_roles` | Roles registered for `implement` |
|
|
43
|
+
| `verification_roles` | Roles registered for `verify`, none of them in `implementation_roles` |
|
|
44
|
+
|
|
45
|
+
Write the frontmatter in the YAML subset the validator reads: one
|
|
46
|
+
`key: value` per line, inline lists such as `[code-analyst, code-reviewer]`,
|
|
47
|
+
quoted strings when a value contains `:` or `#`.
|
|
48
|
+
|
|
49
|
+
## Body
|
|
50
|
+
|
|
51
|
+
- Exactly four level-two sections, in this order: `What to do`, `How to do it`,
|
|
52
|
+
`Participating roles`, `Required quality gates`. Add only details that change
|
|
53
|
+
how the work is done; do not add per-role reports or checklists as a fifth
|
|
54
|
+
section. Implement later adds one `Implementation Record` section below them.
|
|
55
|
+
- Line forms: `REQ-NN | Source: … | Requirement: …`;
|
|
56
|
+
`ROLE-NN | Type: … | Role: … | Responsible for: … | Input: … | Done when: …`;
|
|
57
|
+
`QG-NN | Requirement: REQ-NN | Required logic: …`.
|
|
58
|
+
- Write in English; keep IDs, data keys, symbols, commands and paths verbatim.
|
|
59
|
+
|
|
60
|
+
## Placeholders in the template
|
|
61
|
+
|
|
62
|
+
- `{{name}}`: computed values that `scripts/new_plan.py` fills in. When a file
|
|
63
|
+
is written without the script, fill them by the rules above.
|
|
64
|
+
- `<…>`: content the coordinator writes from the settled decisions. A line of
|
|
65
|
+
the form `<If …: …>` stays only when its condition holds.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create one Plan task file from the plan skill's template, with the computed values filled in.
|
|
3
|
+
|
|
4
|
+
The layout, naming and location rules are owned by ../references/plan-format.md; this script fills only
|
|
5
|
+
what a tool can compute (package name, task ID, file name, timestamp) and leaves every <...> placeholder
|
|
6
|
+
to the coordinator. It never overwrites a file.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
SKILL = Path(__file__).resolve().parents[1]
|
|
19
|
+
HUB = SKILL.parents[1]
|
|
20
|
+
sys.path.insert(0, str(HUB / ".agents" / "evals"))
|
|
21
|
+
from validate_layer_artifact import LayerArtifactError, read_frontmatter, resolve_path # noqa: E402
|
|
22
|
+
|
|
23
|
+
TEMPLATE = SKILL / "assets" / "plan-template.md"
|
|
24
|
+
SLUG = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
|
|
25
|
+
PLAN_SET = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*_(\d{8}-\d{6})")
|
|
26
|
+
TASK = re.compile(r"T(\d{2,})")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def shown(path: Path, root: Path) -> str:
|
|
30
|
+
"""A path as Plans and reports write it: relative to the Hub root when inside it, otherwise absolute."""
|
|
31
|
+
|
|
32
|
+
return path.relative_to(root).as_posix() if path.is_relative_to(root) else path.as_posix()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create(root: Path, todo_slug: str, request_slug: str | None, plan_set: str | None, out_dir: str | None) -> dict[str, str]:
|
|
36
|
+
if not SLUG.fullmatch(todo_slug):
|
|
37
|
+
raise LayerArtifactError("--todo-slug must be ASCII kebab-case")
|
|
38
|
+
base = resolve_path(out_dir, root) if out_dir else root
|
|
39
|
+
now = datetime.now().astimezone()
|
|
40
|
+
if request_slug is not None:
|
|
41
|
+
if not SLUG.fullmatch(request_slug):
|
|
42
|
+
raise LayerArtifactError("--request-slug must be ASCII kebab-case")
|
|
43
|
+
plan_set = f"{request_slug}_{now:%Y%m%d-%H%M%S}"
|
|
44
|
+
package = base / plan_set
|
|
45
|
+
if package.exists():
|
|
46
|
+
raise LayerArtifactError(f"{shown(package, root)} already exists; pass --plan-set to add a task to it")
|
|
47
|
+
else:
|
|
48
|
+
if not plan_set or not PLAN_SET.fullmatch(plan_set):
|
|
49
|
+
raise LayerArtifactError("--plan-set must have the form <request-slug>_<YYYYMMDD-HHmmss>")
|
|
50
|
+
package = base / plan_set
|
|
51
|
+
if not package.is_dir():
|
|
52
|
+
raise LayerArtifactError(f"{shown(package, root)} is not an existing Plan package")
|
|
53
|
+
stamp = PLAN_SET.fullmatch(plan_set).group(1)
|
|
54
|
+
numbers = [0]
|
|
55
|
+
for item in sorted(package.glob("*.md")) if package.is_dir() else []:
|
|
56
|
+
match = TASK.fullmatch(str(read_frontmatter(item)[0].get("task_id", "")))
|
|
57
|
+
if match:
|
|
58
|
+
numbers.append(int(match.group(1)))
|
|
59
|
+
number = max(numbers) + 1
|
|
60
|
+
task_id = f"T{number:02d}"
|
|
61
|
+
target = package / f"{number:02d}-plan-{todo_slug}-{stamp}.md"
|
|
62
|
+
if target.exists():
|
|
63
|
+
raise LayerArtifactError(f"{shown(target, root)} already exists")
|
|
64
|
+
text = (TEMPLATE.read_text(encoding="utf-8")
|
|
65
|
+
.replace("{{plan_set}}", plan_set)
|
|
66
|
+
.replace("{{task_id}}", task_id)
|
|
67
|
+
.replace("{{created_at}}", now.isoformat(timespec="seconds")))
|
|
68
|
+
package.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
target.write_text(text, encoding="utf-8", newline="\n")
|
|
70
|
+
return {"path": shown(target, root), "plan_set": plan_set, "task_id": task_id}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def main() -> int:
|
|
74
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
75
|
+
parser.add_argument("--todo-slug", required=True, help="ASCII kebab-case slug of this task")
|
|
76
|
+
start = parser.add_mutually_exclusive_group(required=True)
|
|
77
|
+
start.add_argument("--request-slug", help="ASCII kebab-case slug of the request; starts a new package")
|
|
78
|
+
start.add_argument("--plan-set", help="name of an existing package; adds the next task to it")
|
|
79
|
+
parser.add_argument("--out-dir", help="directory holding the package, relative to the Hub root or absolute; default: the Hub root")
|
|
80
|
+
parser.add_argument("--root", type=Path, default=HUB, help="Hub root (default: the Hub containing this script)")
|
|
81
|
+
args = parser.parse_args()
|
|
82
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
83
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
84
|
+
try:
|
|
85
|
+
result = {"passed": True, **create(args.root.resolve(), args.todo_slug, args.request_slug, args.plan_set, args.out_dir)}
|
|
86
|
+
except (LayerArtifactError, OSError) as exc:
|
|
87
|
+
result = {"passed": False, "errors": [str(exc)]}
|
|
88
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
89
|
+
return 0 if result["passed"] else 1
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pirl-review
|
|
3
|
+
description: Runs the Review layer of the PIRL framework. Checks one implementation run of a Plan task against the Plan's quality gates by reasoning on the source, without building or running tests, and writes a report with a Business verdict (findings, risks, blockers) and Evaluations of the Implement roles with the framework improvements applied. Use only when the user's own words ask to review the implementation of a named Plan task or to open a new Review round. Do not use to write or implement a Plan, for a general code review outside a Plan or to edit the framework itself.
|
|
4
|
+
metadata:
|
|
5
|
+
id: .skills/pirl-review
|
|
6
|
+
kind: skill
|
|
7
|
+
layer: review
|
|
8
|
+
version: 1.1.2
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Review
|
|
12
|
+
|
|
13
|
+
Carries out one Review round. The
|
|
14
|
+
[Review contract](../../.agents/instructions/review.instructions.md) owns the
|
|
15
|
+
decisions, the rounds and the exit conditions; the
|
|
16
|
+
[router](../../.agents/instructions/layered-task-execution.instructions.md) owns
|
|
17
|
+
the shared contracts; [AGENTS.md](../../AGENTS.md) owns the core rules, roles
|
|
18
|
+
and target repository. This skill owns the order of the steps, the report
|
|
19
|
+
format and the helper script. Where a step names a section, that section
|
|
20
|
+
decides.
|
|
21
|
+
|
|
22
|
+
## When to use
|
|
23
|
+
|
|
24
|
+
- The user's own words ask for a Review of an implementation run, per the
|
|
25
|
+
[conditions for opening a layer](../../AGENTS.md#choosing-a-layer).
|
|
26
|
+
- Not for writing a Plan ([`pirl-plan`](../pirl-plan/SKILL.md)), implementing or
|
|
27
|
+
repairing ([`pirl-implement`](../pirl-implement/SKILL.md)), a code review that no Plan
|
|
28
|
+
covers
|
|
29
|
+
([standalone task](../../.agents/instructions/standalone-and-maintenance.instructions.md#standalone-tasks))
|
|
30
|
+
or [framework maintenance](../../.agents/instructions/standalone-and-maintenance.instructions.md#framework-maintenance).
|
|
31
|
+
|
|
32
|
+
## Resources
|
|
33
|
+
|
|
34
|
+
| Resource | Use |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| [`assets/review-template.md`](assets/review-template.md) | Template of one Review report |
|
|
37
|
+
| [`references/review-format.md`](references/review-format.md) | Frontmatter fields, file naming and location, presentation rules |
|
|
38
|
+
| [`scripts/new_review.py`](scripts/new_review.py) | Creates the report of the next round for a Plan task, with the round, `prior_review`, `plan_file` and timestamp filled in |
|
|
39
|
+
| [`validate_layer_artifact.py`](../../.agents/evals/validate_layer_artifact.py) | Checks the report structure before the final response |
|
|
40
|
+
| [`layer_record.py`](../../.agents/evals/layer_record.py) | Pins the framework version, finds the reviewed Implement record and writes the run's record |
|
|
41
|
+
|
|
42
|
+
## Procedure
|
|
43
|
+
|
|
44
|
+
Run the commands from the Hub root.
|
|
45
|
+
|
|
46
|
+
1. **Open the run.** Apply the [core rules](../../AGENTS.md#core-rules) and
|
|
47
|
+
[CONTEXT.md](../../CONTEXT.md). Run
|
|
48
|
+
`python -B .agents/evals/layer_record.py start` and keep the returned
|
|
49
|
+
`framework_ref`
|
|
50
|
+
([Timing and evaluator](../../.agents/instructions/layered-task-execution.instructions.md#timing-and-evaluator)).
|
|
51
|
+
2. **Read the inputs**
|
|
52
|
+
([Inputs and permissions](../../.agents/instructions/review.instructions.md#inputs-and-permissions)):
|
|
53
|
+
the Plan, its Implementation Record, the diff and source of the Plan's
|
|
54
|
+
`target_repo`, and earlier reports for the same task. Identify the snapshot
|
|
55
|
+
before concluding.
|
|
56
|
+
3. **Round.** Decide whether this work is a new round or belongs to the current
|
|
57
|
+
one ([Report and rounds](../../.agents/instructions/review.instructions.md#report-and-rounds)).
|
|
58
|
+
A new round creates its report in the directory the user named, or by
|
|
59
|
+
default under the Hub root
|
|
60
|
+
([format](references/review-format.md#location-and-naming)); the current
|
|
61
|
+
round edits its existing report:
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
python -B .skills/pirl-review/scripts/new_review.py --plan <Plan file> --todo-slug <todo-slug> [--out-dir <dir>]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
4. **Team.** Pick `review_roles` by the "Select when" column
|
|
68
|
+
([AI Agent roles at the Review layer](../../.agents/instructions/review.instructions.md#ai-agent-roles-at-the-review-layer));
|
|
69
|
+
delegate each role to a separate agent per the
|
|
70
|
+
[assignment principles](../../.agents/instructions/layered-task-execution.instructions.md#assignment-principles).
|
|
71
|
+
5. **Business**
|
|
72
|
+
([contract](../../.agents/instructions/review.instructions.md#business-prove-or-refute-each-suspicion)):
|
|
73
|
+
conclude each QG, keep only grounded FINDs, RISKs and BLOCKs, and write the
|
|
74
|
+
`Business` section.
|
|
75
|
+
6. **Evaluations and self-evaluation.** `process-auditor`, in a separate agent,
|
|
76
|
+
evaluates the roles of the reviewed Implement run
|
|
77
|
+
([Evaluations](../../.agents/instructions/review.instructions.md#evaluations-assessing-implement-roles))
|
|
78
|
+
and the Review run itself
|
|
79
|
+
([Self-evaluation and improvement](../../.agents/instructions/layered-task-execution.instructions.md#self-evaluation-and-improvement)),
|
|
80
|
+
applies improvements and writes the `Evaluations` section. The reviewed
|
|
81
|
+
record is found with:
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
python -B .agents/evals/layer_record.py query --layer implement --plan-set <plan-set> --task <task-id>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
7. **Validate** the report and fix until it passes:
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
python -B .agents/evals/validate_layer_artifact.py --kind review --path <report file>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
8. **Write the record** of the round, with `evaluates` and `task.report_file`
|
|
94
|
+
set:
|
|
95
|
+
|
|
96
|
+
```powershell
|
|
97
|
+
python -B .agents/evals/layer_record.py append --input <JSON file>
|
|
98
|
+
python -B .agents/evals/layer_record.py check
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
9. **Clean up** temporary resources
|
|
102
|
+
([router](../../.agents/instructions/layered-task-execution.instructions.md#cleaning-up-temporary-resources-before-handoff)).
|
|
103
|
+
10. **Report**
|
|
104
|
+
([Finishing and next permissions](../../.agents/instructions/review.instructions.md#finishing-and-next-permissions),
|
|
105
|
+
[Reporting](../../.agents/instructions/layered-task-execution.instructions.md#reporting)).
|
|
106
|
+
Stop there: a repair run or a new round opens only when the user asks.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema: pirl-review/v1
|
|
3
|
+
content_language: en
|
|
4
|
+
plan_file: "{{plan_file}}"
|
|
5
|
+
task_id: {{task_id}}
|
|
6
|
+
review_round: {{review_round}}
|
|
7
|
+
prior_review: {{prior_review}}
|
|
8
|
+
review_roles: [code-analyst, code-reviewer, business-analyst, solution-architect]
|
|
9
|
+
repair_roles: []
|
|
10
|
+
business_verdict: PASS
|
|
11
|
+
verdict: PASS
|
|
12
|
+
status: completed
|
|
13
|
+
reviewed_at: "{{reviewed_at}}"
|
|
14
|
+
implementation_target: "<diff/commit/paths reviewed>"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Review {{review_round_padded}}: {{task_id}} — {{title}}
|
|
18
|
+
|
|
19
|
+
## Business
|
|
20
|
+
|
|
21
|
+
### Quality gates
|
|
22
|
+
|
|
23
|
+
| QG | Result | Evidence |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| QG-01 | <PASS / FAIL / BLOCKED> | <source/symbol, logic and the result of the check> |
|
|
26
|
+
|
|
27
|
+
### Findings
|
|
28
|
+
|
|
29
|
+
#### FIND-01
|
|
30
|
+
|
|
31
|
+
| Field | Content |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| QG | QG-01 |
|
|
34
|
+
| Impact | <realistic trigger and consequence> |
|
|
35
|
+
| Source | <file/symbol or trusted contract> |
|
|
36
|
+
| Cause | <the faulty mechanism> |
|
|
37
|
+
| Challenge | <grounds that refute competing explanations> |
|
|
38
|
+
| Roles | <role, ...> |
|
|
39
|
+
| Target | `<file to handle>` |
|
|
40
|
+
| Instructions | <concrete work for each role, in order> |
|
|
41
|
+
| Disposition | <repair / replan> |
|
|
42
|
+
|
|
43
|
+
### Risks
|
|
44
|
+
|
|
45
|
+
#### RISK-01
|
|
46
|
+
|
|
47
|
+
| Field | Content |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| QG | QG-02 |
|
|
50
|
+
| Impact | <realistic exposure and limits> |
|
|
51
|
+
| Source | <file/symbol> |
|
|
52
|
+
| Cause | <the mechanism that creates the risk> |
|
|
53
|
+
| Challenge | <why the risk stands while the QG still holds> |
|
|
54
|
+
| Roles | <role, ...> |
|
|
55
|
+
| Target | `<related file>` |
|
|
56
|
+
| Instructions | <how to mitigate when assigned> |
|
|
57
|
+
| Revisit when | <concrete condition> |
|
|
58
|
+
|
|
59
|
+
### Blockers
|
|
60
|
+
|
|
61
|
+
#### BLOCK-01
|
|
62
|
+
|
|
63
|
+
| Field | Content |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| QG | QG-03 |
|
|
66
|
+
| Missing | <mandatory input not yet available> |
|
|
67
|
+
| Source | <evidence of what is missing> |
|
|
68
|
+
| Cause | <the mechanism that blocks the conclusion> |
|
|
69
|
+
| Owner | <role or provider of the input> |
|
|
70
|
+
| Target | `<related file>` |
|
|
71
|
+
| Instructions | <action to unblock> |
|
|
72
|
+
| Disposition | <retry / escalate> |
|
|
73
|
+
| Condition | <change needed before continuing> |
|
|
74
|
+
|
|
75
|
+
### Next
|
|
76
|
+
|
|
77
|
+
| Field | Content |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| Next | <owner/layer, input file and condition to continue> |
|
|
80
|
+
|
|
81
|
+
## Evaluations
|
|
82
|
+
|
|
83
|
+
### EVAL-01
|
|
84
|
+
|
|
85
|
+
| Field | Content |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| Role | <Implement implementation or verification role> |
|
|
88
|
+
| Result | <PASS / FAIL / BLOCKED> |
|
|
89
|
+
| Issue | <the role's grounded wrong decision; or none> |
|
|
90
|
+
| Cause | <instruction / ineffective_fix / input / execution with `issue_key`; or none> |
|
|
91
|
+
| Improvement | <file and section edited, old → new version; or pending the user (safety); or not needed> |
|
|
92
|
+
| Evidence | <Implementation Record, Implement record, sources/diff checked> |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Review report format
|
|
2
|
+
|
|
3
|
+
The `pirl-review` skill owns how a Review report is laid out, named, stored and
|
|
4
|
+
presented. What each conclusion must prove, and when a new round opens, belongs
|
|
5
|
+
to the [Review contract](../../../.agents/instructions/review.instructions.md).
|
|
6
|
+
|
|
7
|
+
## Location and naming
|
|
8
|
+
|
|
9
|
+
- Output directory: the directory the user names for the report; when the user
|
|
10
|
+
names none, the Hub root.
|
|
11
|
+
- Report:
|
|
12
|
+
`<output directory>/<plan-set>/<task-id-lower>-<todo-slug>/round-<NN>-review-<todo-slug>-<YYYYMMDD-HHmmss>.md`,
|
|
13
|
+
where `NN` is the two-digit round number. When the Plan package also sits
|
|
14
|
+
under the Hub root, the report lands inside the package directory, next to
|
|
15
|
+
the Plan it reviews. Inside the Hub, the `<plan-set>/` directory is ignored by
|
|
16
|
+
Git like the Plan package ([AGENTS.md](../../../AGENTS.md#git)).
|
|
17
|
+
- Slugs are ASCII kebab-case; timestamps use the machine's local
|
|
18
|
+
time zone and carry its UTC offset.
|
|
19
|
+
- Every round has its own file; never overwrite or delete another round.
|
|
20
|
+
|
|
21
|
+
## Frontmatter
|
|
22
|
+
|
|
23
|
+
| Field | Value |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `schema` | `pirl-review/v1` |
|
|
26
|
+
| `content_language` | `en` |
|
|
27
|
+
| `plan_file` | The reviewed Plan file: path relative to the Hub root, or absolute when it lives outside the Hub; quoted |
|
|
28
|
+
| `task_id` | The Plan's `task_id` |
|
|
29
|
+
| `review_round` | `1`, `2`, … |
|
|
30
|
+
| `prior_review` | `null` in round 1; afterwards the report of the round just before, as a path like `plan_file` |
|
|
31
|
+
| `review_roles` | Roles registered for `review`, in the order of the contract's role table |
|
|
32
|
+
| `repair_roles` | Roles registered for `implement`; `[]` while a `replan` remains or nothing needs repair |
|
|
33
|
+
| `business_verdict` | `PASS`, `FAIL` or `BLOCKED` |
|
|
34
|
+
| `verdict` | Equal to `business_verdict` |
|
|
35
|
+
| `status` | `completed` or `blocked` |
|
|
36
|
+
| `reviewed_at` | ISO-8601 timestamp with a UTC offset, quoted |
|
|
37
|
+
| `implementation_target` | The diff, commit or paths reviewed, including a shared snapshot |
|
|
38
|
+
|
|
39
|
+
## Presentation rules
|
|
40
|
+
|
|
41
|
+
Keep the two level-two sections `Business` and `Evaluations`; use level-three/
|
|
42
|
+
four headings to divide the content inside. Present QGs as a three-column
|
|
43
|
+
table; each FIND/RISK/BLOCK and EVAL has its own two-column `Field | Content`
|
|
44
|
+
table under a heading containing its ID. The `Next` part uses a two-column
|
|
45
|
+
table. Do not merge several fields into one long bullet or cram all fields of a
|
|
46
|
+
finding/evaluation into one overly wide table.
|
|
47
|
+
|
|
48
|
+
This is only presentation: keep all fields, IDs, conclusions, evidence and
|
|
49
|
+
instructions; do not drop information to make a table shorter. In each cell,
|
|
50
|
+
use `<br>` when you need to separate several sources or processing steps; keep
|
|
51
|
+
paths, symbols and commands intact. Escape a `|` that belongs to cell content
|
|
52
|
+
as `\|`, including inside inline code, so it does not create a column. This
|
|
53
|
+
escape rule applies only to Markdown tables and does not change values in the
|
|
54
|
+
frontmatter. Put a blank line before/after each table.
|
|
55
|
+
|
|
56
|
+
Replace placeholders with actual content and choose one value for each status
|
|
57
|
+
or disposition. Each QG has exactly one row in the Quality gates table. Record
|
|
58
|
+
only FINDs/RISKs/BLOCKs that actually exist; a group with no items uses one
|
|
59
|
+
short sentence under its heading, with no empty table or fake ID. Number each
|
|
60
|
+
kind consecutively, keeping the order findings, risks, then blockers. EVALs
|
|
61
|
+
cover the settled Implement team in the Implementation Record's order; do not
|
|
62
|
+
add an inventory or a third level-two section that repeats the handoff.
|
|
63
|
+
|
|
64
|
+
## Placeholders in the template
|
|
65
|
+
|
|
66
|
+
- `{{name}}`: computed values that `scripts/new_review.py` fills in from the
|
|
67
|
+
Plan and the earlier rounds. When a report is written without the script,
|
|
68
|
+
fill them by the rules above.
|
|
69
|
+
- `<…>` and the `a / b` alternatives: the coordinator writes the actual content
|
|
70
|
+
and keeps exactly one alternative.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create the report of the next Review round for a Plan task from the review skill's template.
|
|
3
|
+
|
|
4
|
+
The layout, naming and location rules are owned by ../references/review-format.md; this script fills only
|
|
5
|
+
what a tool can compute (plan_file, task ID, round, prior_review, timestamp, title) and leaves every <...>
|
|
6
|
+
placeholder to the coordinator. Work that belongs to the current round edits that round's report instead.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
SKILL = Path(__file__).resolve().parents[1]
|
|
19
|
+
HUB = SKILL.parents[1]
|
|
20
|
+
sys.path.insert(0, str(HUB / ".agents" / "evals"))
|
|
21
|
+
from validate_layer_artifact import PLAN_SCHEMA, LayerArtifactError, read_frontmatter, resolve_path # noqa: E402
|
|
22
|
+
|
|
23
|
+
TEMPLATE = SKILL / "assets" / "review-template.md"
|
|
24
|
+
SLUG = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
|
|
25
|
+
ROUND = re.compile(r"round-(\d{2,})-review-.+\.md")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def shown(path: Path, root: Path) -> str:
|
|
29
|
+
"""A path as Plans and reports write it: relative to the Hub root when inside it, otherwise absolute."""
|
|
30
|
+
|
|
31
|
+
return path.relative_to(root).as_posix() if path.is_relative_to(root) else path.as_posix()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create(root: Path, plan: str, todo_slug: str, out_dir: str | None) -> dict[str, object]:
|
|
35
|
+
if not SLUG.fullmatch(todo_slug):
|
|
36
|
+
raise LayerArtifactError("--todo-slug must be ASCII kebab-case")
|
|
37
|
+
plan_path = resolve_path(plan, root)
|
|
38
|
+
if not plan_path.is_file():
|
|
39
|
+
raise LayerArtifactError(f"{plan} is not an existing Plan file (relative to the Hub root or absolute)")
|
|
40
|
+
metadata, _ = read_frontmatter(plan_path)
|
|
41
|
+
if metadata.get("schema") != PLAN_SCHEMA or not metadata.get("plan_set") or not metadata.get("task_id"):
|
|
42
|
+
raise LayerArtifactError(f"{plan} is not a {PLAN_SCHEMA} Plan with plan_set and task_id")
|
|
43
|
+
task_id = str(metadata["task_id"])
|
|
44
|
+
folder = (resolve_path(out_dir, root) if out_dir else root) / str(metadata["plan_set"]) / f"{task_id.lower()}-{todo_slug}"
|
|
45
|
+
rounds = sorted((int(m.group(1)), p) for p in (folder.glob("*.md") if folder.is_dir() else []) if (m := ROUND.fullmatch(p.name)))
|
|
46
|
+
number = rounds[-1][0] + 1 if rounds else 1
|
|
47
|
+
prior = json.dumps(shown(rounds[-1][1], root), ensure_ascii=False) if rounds else "null"
|
|
48
|
+
now = datetime.now().astimezone()
|
|
49
|
+
target = folder / f"round-{number:02d}-review-{todo_slug}-{now:%Y%m%d-%H%M%S}.md"
|
|
50
|
+
if target.exists():
|
|
51
|
+
raise LayerArtifactError(f"{shown(target, root)} already exists")
|
|
52
|
+
text = (TEMPLATE.read_text(encoding="utf-8")
|
|
53
|
+
.replace("{{plan_file}}", shown(plan_path, root))
|
|
54
|
+
.replace("{{task_id}}", task_id)
|
|
55
|
+
.replace("{{review_round_padded}}", f"{number:02d}")
|
|
56
|
+
.replace("{{review_round}}", str(number))
|
|
57
|
+
.replace("{{prior_review}}", prior)
|
|
58
|
+
.replace("{{reviewed_at}}", now.isoformat(timespec="seconds"))
|
|
59
|
+
.replace("{{title}}", str(metadata.get("title") or "<Task name>")))
|
|
60
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
target.write_text(text, encoding="utf-8", newline="\n")
|
|
62
|
+
return {"path": shown(target, root), "review_round": number, "prior_review": json.loads(prior)}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def main() -> int:
|
|
66
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
67
|
+
parser.add_argument("--plan", required=True, help="the reviewed Plan file, relative to the Hub root or absolute")
|
|
68
|
+
parser.add_argument("--todo-slug", required=True, help="ASCII kebab-case slug of the reviewed task")
|
|
69
|
+
parser.add_argument("--out-dir", help="directory holding the report's package folder, relative to the Hub root or absolute; default: the Hub root")
|
|
70
|
+
parser.add_argument("--root", type=Path, default=HUB, help="Hub root (default: the Hub containing this script)")
|
|
71
|
+
args = parser.parse_args()
|
|
72
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
73
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
74
|
+
try:
|
|
75
|
+
result = {"passed": True, **create(args.root.resolve(), args.plan, args.todo_slug, args.out_dir)}
|
|
76
|
+
except (LayerArtifactError, OSError) as exc:
|
|
77
|
+
result = {"passed": False, "errors": [str(exc)]}
|
|
78
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
79
|
+
return 0 if result["passed"] else 1
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
raise SystemExit(main())
|