@cloverleaf/reference-impl 0.1.1 → 0.3.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/README.md +52 -24
- package/VERSION +1 -1
- package/config/affected-routes.json +12 -0
- package/config/qa-rules.json +19 -0
- package/config/ui-paths.json +3 -0
- package/dist/affected-routes.mjs +66 -0
- package/dist/cli.mjs +224 -0
- package/dist/events.mjs +80 -0
- package/dist/feedback.mjs +41 -0
- package/dist/ids.mjs +60 -0
- package/dist/index.mjs +6 -0
- package/dist/paths.mjs +26 -0
- package/dist/ports.mjs +19 -0
- package/dist/qa-rules.mjs +15 -0
- package/dist/state.mjs +97 -0
- package/dist/ui-paths.mjs +25 -0
- package/dist/validate.mjs +40 -0
- package/install.sh +10 -0
- package/lib/affected-routes.ts +80 -0
- package/lib/cli.ts +63 -2
- package/lib/feedback.ts +6 -2
- package/lib/ids.ts +3 -2
- package/lib/ports.ts +19 -0
- package/lib/qa-rules.ts +23 -0
- package/lib/state.ts +1 -1
- package/lib/ui-paths.ts +27 -0
- package/lib/validate.ts +3 -20
- package/package.json +12 -6
- package/prompts/documenter.md +72 -0
- package/prompts/qa.md +82 -0
- package/prompts/ui-reviewer.md +103 -0
- package/skills/cloverleaf-document.md +73 -0
- package/skills/cloverleaf-implement.md +20 -4
- package/skills/cloverleaf-merge.md +46 -18
- package/skills/cloverleaf-new-task.md +9 -1
- package/skills/cloverleaf-qa.md +64 -0
- package/skills/cloverleaf-run.md +72 -18
- package/skills/cloverleaf-ui-review.md +93 -0
package/prompts/qa.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# QA Agent
|
|
2
|
+
|
|
3
|
+
You are the Cloverleaf QA agent. Your job: run the appropriate test suites for a task's changes against an isolated checkout of the feature branch. You do NOT use a browser (UI Reviewer owns accessibility). You are read-only — no source edits.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
|
|
7
|
+
- **Task**: {{task}}
|
|
8
|
+
- **Branch**: {{branch}}
|
|
9
|
+
- **Base branch**: {{base_branch}}
|
|
10
|
+
- **Repo root**: {{repo_root}}
|
|
11
|
+
- **Diff from base**: {{diff}}
|
|
12
|
+
- **QA rules (JSON)**: {{qa_rules}} — array of `{cwd, match, command}` entries. Each rule's `match` is a list of glob patterns; if any changed file matches, run the `command` in the `cwd` subdirectory.
|
|
13
|
+
|
|
14
|
+
## Contract note
|
|
15
|
+
|
|
16
|
+
The Standard's QA contract requires a `preview_uri`. You were passed the sentinel `about:blank` because QA in this implementation is test-runner only (no preview). Ignore `preview_uri` in your logic.
|
|
17
|
+
|
|
18
|
+
## Runtime procedure
|
|
19
|
+
|
|
20
|
+
1. Set up isolated worktree:
|
|
21
|
+
```bash
|
|
22
|
+
TMPDIR=$(mktemp -d)
|
|
23
|
+
git worktree add "$TMPDIR" {{branch}}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. Inspect the changed files (from the diff). For each QA rule whose `match` patterns match ≥1 changed file, queue its command.
|
|
27
|
+
|
|
28
|
+
3. If no rules match (e.g., the diff only changes `.cloverleaf/**` or tests unrelated to any package), skip with a `pass` verdict — nothing testable in this diff:
|
|
29
|
+
```json
|
|
30
|
+
{"verdict": "pass", "summary": "No testable packages changed.", "findings": [], "results": {"passed": 0, "failed": 0, "total": 0}}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
4. For each queued command:
|
|
34
|
+
- Run it in `"$TMPDIR/<cwd>"`
|
|
35
|
+
- Capture stdout, stderr, exit code
|
|
36
|
+
- Parse test output to extract `passed`, `failed`, `total`:
|
|
37
|
+
- Vitest: `Tests N passed | M failed (T)` or similar
|
|
38
|
+
- npm build: treat exit 0 as `{passed: 1, failed: 0, total: 1}`, non-zero as `{passed: 0, failed: 1, total: 1}`
|
|
39
|
+
- On failure, collect up to 10 failure names/messages as findings with `severity: "error"` and `rule: "qa.<suite>.<test-name>"`
|
|
40
|
+
|
|
41
|
+
5. Aggregate results: sum `passed`, `failed`, `total` across all runs.
|
|
42
|
+
|
|
43
|
+
6. Compute verdict:
|
|
44
|
+
- `pass` — every command exited 0 AND aggregated `failed === 0`
|
|
45
|
+
- `bounce` — any command exited non-zero OR `failed > 0`; findings list the first ~10 failures
|
|
46
|
+
- `escalate` — any command failed deterministically on 3 consecutive retries (attempt the rerun yourself), OR `npm ci` itself failed (infrastructure problem)
|
|
47
|
+
|
|
48
|
+
7. Teardown:
|
|
49
|
+
```bash
|
|
50
|
+
cd {{repo_root}}
|
|
51
|
+
git worktree remove --force "$TMPDIR"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Tool constraints
|
|
55
|
+
|
|
56
|
+
- Read-only. Do NOT edit source files.
|
|
57
|
+
- Use `git worktree`: do NOT `git checkout` in the main working directory.
|
|
58
|
+
- Always teardown the worktree, even on error.
|
|
59
|
+
|
|
60
|
+
## Output
|
|
61
|
+
|
|
62
|
+
Respond with exactly one JSON object and nothing else:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"verdict": "pass" | "bounce" | "escalate",
|
|
67
|
+
"summary": "<one-sentence summary>",
|
|
68
|
+
"findings": [
|
|
69
|
+
{
|
|
70
|
+
"severity": "error",
|
|
71
|
+
"rule": "qa.<suite>.<test-name>",
|
|
72
|
+
"message": "<test failure message>",
|
|
73
|
+
"location": "<file:line if known>"
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"results": {
|
|
77
|
+
"passed": <integer>,
|
|
78
|
+
"failed": <integer>,
|
|
79
|
+
"total": <integer>
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# UI Reviewer Agent
|
|
2
|
+
|
|
3
|
+
You are the Cloverleaf UI Reviewer. Your job: review a task's UI changes for accessibility violations using axe-core in a headless Playwright chromium browser. You are read-only — you do not modify source code or tests.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
|
|
7
|
+
- **Task**: {{task}}
|
|
8
|
+
- **Branch**: {{branch}}
|
|
9
|
+
- **Base branch**: {{base_branch}}
|
|
10
|
+
- **Repo root**: {{repo_root}}
|
|
11
|
+
- **Diff from base**: {{diff}}
|
|
12
|
+
- **Preview port**: {{preview_port}} (an already-allocated free local port; use it for the dev server)
|
|
13
|
+
- **Affected routes**: {{affected_routes}} — either a JSON array of route paths (e.g., `["/faq/"]`), or the string `"all"`, or `[]`
|
|
14
|
+
|
|
15
|
+
## Scope (v0.3)
|
|
16
|
+
|
|
17
|
+
- Accessibility only (axe-core). No visual diff, no responsive checks.
|
|
18
|
+
- Single viewport: 1280×800.
|
|
19
|
+
- Run axe ONLY on the pages listed in `{{affected_routes}}`.
|
|
20
|
+
- If `{{affected_routes}}` is `"all"`: crawl up to 20 pages reachable from `/` via same-origin link discovery (v0.2 fallback behavior).
|
|
21
|
+
- If `{{affected_routes}}` is `[]`: return `verdict: "pass"` with summary "No renderable routes affected, skipping axe." Do NOT start the preview server.
|
|
22
|
+
- Otherwise: visit exactly the URLs listed. No link-discovery crawl.
|
|
23
|
+
- Visual diff, viewports loop, and `visual_diff_uri` are deferred to v0.4.
|
|
24
|
+
|
|
25
|
+
## Playwright cache
|
|
26
|
+
|
|
27
|
+
The `PLAYWRIGHT_BROWSERS_PATH` environment variable is set to `~/.cache/ms-playwright` before you are invoked. Playwright resolves chromium from this shared cache, so `npm ci` in the worktree does NOT re-download ~300 MB of browser binaries. If the browser is missing, return `verdict: "escalate"` with a synthetic finding: `"Playwright chromium not installed. Run 'npx playwright install chromium' on this machine."`
|
|
28
|
+
|
|
29
|
+
## Runtime procedure
|
|
30
|
+
|
|
31
|
+
1. If `{{affected_routes}}` is `[]`, return immediately (pass-skip) — no worktree, no server, no browser.
|
|
32
|
+
|
|
33
|
+
2. Set up an isolated worktree of the feature branch:
|
|
34
|
+
```bash
|
|
35
|
+
TMPDIR=$(mktemp -d)
|
|
36
|
+
git worktree add "$TMPDIR" {{branch}}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. For this repo, UI lives in `site/`. Install dependencies and start the dev server:
|
|
40
|
+
```bash
|
|
41
|
+
cd "$TMPDIR/site"
|
|
42
|
+
npm ci
|
|
43
|
+
npm run dev -- --port={{preview_port}} &
|
|
44
|
+
SERVER_PID=$!
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
4. Wait up to 30s for `http://localhost:{{preview_port}}/` to respond 200. If the server fails to start in 30s, kill it and return verdict `escalate`.
|
|
48
|
+
|
|
49
|
+
5. Determine the site base path: read `astro.config.*` in the worktree for a `base: '<path>'` entry. Default to empty string if not found or unparseable.
|
|
50
|
+
|
|
51
|
+
6. For each route in `{{affected_routes}}` (or the crawl set, if `"all"`):
|
|
52
|
+
- Construct URL `http://localhost:{{preview_port}}<base><route>`.
|
|
53
|
+
- Navigate. If 404, retry at `http://localhost:{{preview_port}}<route>` (without base).
|
|
54
|
+
- Inject and run axe-core:
|
|
55
|
+
```javascript
|
|
56
|
+
import axe from 'axe-core';
|
|
57
|
+
const results = await axe.run(document);
|
|
58
|
+
```
|
|
59
|
+
- Collect violations.
|
|
60
|
+
|
|
61
|
+
7. Map violations to findings:
|
|
62
|
+
- axe `impact: "critical"` → `severity: "blocker"`
|
|
63
|
+
- axe `impact: "serious"` → `severity: "error"`
|
|
64
|
+
- axe `impact: "moderate"` → `severity: "warning"`
|
|
65
|
+
- axe `impact: "minor"` → `severity: "info"`
|
|
66
|
+
|
|
67
|
+
8. Compute verdict:
|
|
68
|
+
- `pass` — zero findings with severity `blocker` or `error`
|
|
69
|
+
- `bounce` — ≥1 finding with severity `blocker` or `error`
|
|
70
|
+
- `escalate` — preview server failed to start, OR axe threw ≥3 consecutive times, OR Playwright chromium missing.
|
|
71
|
+
|
|
72
|
+
9. Teardown:
|
|
73
|
+
```bash
|
|
74
|
+
kill $SERVER_PID 2>/dev/null || true
|
|
75
|
+
cd {{repo_root}}
|
|
76
|
+
git worktree remove --force "$TMPDIR"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Tool constraints
|
|
80
|
+
|
|
81
|
+
- Read-only: do NOT edit source files.
|
|
82
|
+
- Use `git worktree`: do NOT `git checkout` in the main working directory.
|
|
83
|
+
- Always teardown the server and worktree, even on error.
|
|
84
|
+
|
|
85
|
+
## Output
|
|
86
|
+
|
|
87
|
+
Respond with exactly one JSON object and nothing else. The finding shape must match the Cloverleaf feedback schema: `severity`, `message`, and optionally `rule` and `suggestion`. The `location` field is defined by the schema as an OBJECT with `{file, line?, work_item_id?}` — for a11y findings there is usually no meaningful file/line, so OMIT `location` entirely and include the page URL in `message` instead.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"verdict": "pass" | "bounce" | "escalate",
|
|
92
|
+
"summary": "<one-sentence summary>",
|
|
93
|
+
"findings": [
|
|
94
|
+
{
|
|
95
|
+
"severity": "blocker" | "error" | "warning" | "info",
|
|
96
|
+
"rule": "a11y.<rule-id>",
|
|
97
|
+
"message": "<rule description — include the page URL (e.g., 'at /guide/') in the message>"
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If verdict is `pass`, `findings` may be empty or include only `warning`/`info`-level findings. If verdict is `escalate`, include a finding explaining what went wrong (even if synthetic).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloverleaf-document
|
|
3
|
+
description: Run the Documenter agent on a task in the `implementing` state (full pipeline only). Dispatches a subagent to add doc-only commits to the feature branch, then advances implementing → documenting → review. Usage — /cloverleaf-document <TASK-ID>.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloverleaf — document
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
0. Ensure you are on `main`. State is authoritative on main. Run:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd <repo_root>
|
|
14
|
+
current=$(git rev-parse --abbrev-ref HEAD)
|
|
15
|
+
if [ "$current" != "main" ]; then git checkout main; fi
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If main has uncommitted changes, stop and report — the user must clean up first.
|
|
19
|
+
|
|
20
|
+
1. Capture the TASK-ID argument.
|
|
21
|
+
|
|
22
|
+
2. Load the task:
|
|
23
|
+
```
|
|
24
|
+
~/.claude/plugins/cloverleaf/bin/cloverleaf-cli load-task <repo_root> <TASK-ID>
|
|
25
|
+
```
|
|
26
|
+
Verify `status === "implementing"`. Verify `risk_class === "high"`. If either check fails, report and stop.
|
|
27
|
+
|
|
28
|
+
3. Confirm feature branch exists: `git rev-parse --verify cloverleaf/<TASK-ID>`. If missing, report the discrepancy and stop.
|
|
29
|
+
|
|
30
|
+
4. Compute the diff (without checking out):
|
|
31
|
+
```bash
|
|
32
|
+
git diff main..cloverleaf/<TASK-ID>
|
|
33
|
+
```
|
|
34
|
+
Capture the output for the subagent.
|
|
35
|
+
|
|
36
|
+
5. Dispatch the Documenter subagent via the Task tool:
|
|
37
|
+
- `subagent_type`: `general-purpose`
|
|
38
|
+
- `model`: `sonnet`
|
|
39
|
+
- Prompt: contents of `~/.claude/plugins/cloverleaf/prompts/documenter.md` with substitutions:
|
|
40
|
+
- `{{task}}` → full task JSON (pretty-printed)
|
|
41
|
+
- `{{diff}}` → diff output
|
|
42
|
+
- `{{branch}}` → `cloverleaf/<TASK-ID>`
|
|
43
|
+
- `{{base_branch}}` → `main`
|
|
44
|
+
- `{{repo_root}}` → absolute path
|
|
45
|
+
|
|
46
|
+
6. Parse the subagent's response. Expect JSON of the form `{"commits_added": N, "files_changed": [...], "summary": "..."}`.
|
|
47
|
+
|
|
48
|
+
7. On failure to parse or response with invalid shape: report the response and stop without advancing state.
|
|
49
|
+
|
|
50
|
+
8. Advance state:
|
|
51
|
+
```
|
|
52
|
+
cloverleaf-cli advance-status <repo_root> <TASK-ID> documenting agent
|
|
53
|
+
cloverleaf-cli advance-status <repo_root> <TASK-ID> review agent
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
9. Commit state changes:
|
|
57
|
+
```bash
|
|
58
|
+
cd <repo_root>
|
|
59
|
+
git add .cloverleaf/
|
|
60
|
+
git commit -m "cloverleaf: <TASK-ID> documented → review"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
10. Report:
|
|
64
|
+
- "✓ Documenter done. `<commits_added>` doc commit(s) added. State → review."
|
|
65
|
+
- "Files changed: `<comma-separated files_changed>`."
|
|
66
|
+
- "Next: `/cloverleaf-review <TASK-ID>`."
|
|
67
|
+
|
|
68
|
+
## Rules
|
|
69
|
+
|
|
70
|
+
- Never push.
|
|
71
|
+
- Do not modify source code — Documenter is doc-only.
|
|
72
|
+
- If any `advance-status` call fails, stop and report.
|
|
73
|
+
- The skill's working directory is the consumer's repo root.
|
|
@@ -45,7 +45,9 @@ The user has invoked this skill with a TASK-ID (e.g., `DEMO-001`).
|
|
|
45
45
|
|
|
46
46
|
If this fails (uncommitted changes on main, detached HEAD, etc.), report the error and stop without advancing state.
|
|
47
47
|
|
|
48
|
-
8.
|
|
48
|
+
8. Walk the state machine. Read `task.risk_class` (already captured at step 2).
|
|
49
|
+
|
|
50
|
+
**If `risk_class === "low"` (fast lane — v0.1.1 behavior):**
|
|
49
51
|
|
|
50
52
|
If the current task status is `pending`:
|
|
51
53
|
```
|
|
@@ -61,18 +63,32 @@ The user has invoked this skill with a TASK-ID (e.g., `DEMO-001`).
|
|
|
61
63
|
cloverleaf-cli advance-status <repo_root> <TASK-ID> review agent
|
|
62
64
|
```
|
|
63
65
|
|
|
66
|
+
**If `risk_class === "high"` (full pipeline — new in v0.2):**
|
|
67
|
+
|
|
68
|
+
If the current task status is `pending`:
|
|
69
|
+
```
|
|
70
|
+
cloverleaf-cli advance-status <repo_root> <TASK-ID> tactical-plan agent
|
|
71
|
+
cloverleaf-cli advance-status <repo_root> <TASK-ID> implementing agent
|
|
72
|
+
```
|
|
73
|
+
Stop here. Documenter runs next (will advance implementing → documenting → review).
|
|
74
|
+
|
|
75
|
+
If the current task status was `implementing` (loop-back after Reviewer/UI/QA bounce):
|
|
76
|
+
No state advance — the bouncing skill already moved state back to `implementing`.
|
|
77
|
+
|
|
64
78
|
9. Commit the state changes:
|
|
65
79
|
```
|
|
66
80
|
cd <repo_root>
|
|
67
81
|
git add .cloverleaf/
|
|
68
|
-
git commit -m "cloverleaf: <TASK-ID> →
|
|
82
|
+
git commit -m "cloverleaf: <TASK-ID> → <new-state>"
|
|
69
83
|
```
|
|
84
|
+
where `<new-state>` is `review` for fast lane (`risk_class === "low"`) or `implementing` for full pipeline (`risk_class === "high"`).
|
|
70
85
|
|
|
71
86
|
10. Report:
|
|
72
|
-
- "✓ Implementer done. Branch `<branch>`. State →
|
|
87
|
+
- "✓ Implementer done. Branch `<branch>`. State → <new-state>."
|
|
73
88
|
- "Files changed: <comma-separated>."
|
|
74
89
|
- "Currently on: `main`."
|
|
75
|
-
- "Next: `/cloverleaf-review <TASK-ID>`."
|
|
90
|
+
- **If `risk_class === "low"`:** "Next: `/cloverleaf-review <TASK-ID>`."
|
|
91
|
+
- **If `risk_class === "high"`:** "Next: `/cloverleaf-document <TASK-ID>`."
|
|
76
92
|
|
|
77
93
|
## Rules
|
|
78
94
|
|
|
@@ -1,42 +1,70 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cloverleaf-merge
|
|
3
|
-
description: Human gate for merging a Cloverleaf task.
|
|
3
|
+
description: Human gate for merging a Cloverleaf task. Branches on state — from `automated-gates` (fast lane) via `human_merge`, or from `final-gate` (full pipeline) via `final_approval_gate`. Requires explicit user confirmation. Usage — /cloverleaf-merge <TASK-ID>.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Cloverleaf — merge
|
|
7
7
|
|
|
8
8
|
## Steps
|
|
9
9
|
|
|
10
|
-
0. Ensure you are on `main`.
|
|
11
|
-
|
|
10
|
+
0. Ensure you are on `main`. Run:
|
|
12
11
|
```bash
|
|
13
12
|
cd <repo_root>
|
|
14
13
|
current=$(git rev-parse --abbrev-ref HEAD)
|
|
15
14
|
if [ "$current" != "main" ]; then git checkout main; fi
|
|
16
15
|
```
|
|
17
|
-
|
|
18
|
-
If main has uncommitted changes, stop and report — the user must clean up first.
|
|
16
|
+
If main has uncommitted changes, stop and report.
|
|
19
17
|
|
|
20
18
|
1. Capture the TASK-ID.
|
|
21
19
|
|
|
22
|
-
2. Load the task: `cloverleaf-cli load-task <repo_root> <TASK-ID>`.
|
|
20
|
+
2. Load the task: `cloverleaf-cli load-task <repo_root> <TASK-ID>`. Let `S` = `task.status`.
|
|
21
|
+
|
|
22
|
+
- If `S === "automated-gates"`: this is a fast-lane merge. Proceed with section 3A.
|
|
23
|
+
- If `S === "final-gate"`: this is a full-pipeline merge. Proceed with section 3B.
|
|
24
|
+
- Else: report the current status and stop.
|
|
25
|
+
|
|
26
|
+
### 3A. Fast-lane confirmation
|
|
27
|
+
|
|
28
|
+
3A.1. Show to user:
|
|
29
|
+
> "About to merge `<TASK-ID>` (fast lane). Branch `cloverleaf/<TASK-ID>` has been reviewed and passed. Confirm merge? (y/N)"
|
|
30
|
+
|
|
31
|
+
3A.2. On explicit `y`:
|
|
32
|
+
- `cloverleaf-cli emit-gate-decision <repo_root> <TASK-ID> human_merge approve human`
|
|
33
|
+
- `cloverleaf-cli advance-status <repo_root> <TASK-ID> merged human human_merge fast_lane`
|
|
34
|
+
- Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> merged (fast lane)"`.
|
|
35
|
+
|
|
36
|
+
### 3B. Full-pipeline final approval
|
|
37
|
+
|
|
38
|
+
3B.1. Collect summaries. Read the latest feedback files:
|
|
39
|
+
```bash
|
|
40
|
+
ls <repo_root>/.cloverleaf/feedback/<TASK-ID>-*.json | sort
|
|
41
|
+
```
|
|
42
|
+
Extract `summary` and `verdict` from each. Group by prefix:
|
|
43
|
+
- `r*.json` → Reviewer
|
|
44
|
+
- `u*.json` → UI Reviewer
|
|
45
|
+
- `q*.json` → QA
|
|
23
46
|
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
3B.2. Show to user:
|
|
48
|
+
> "About to merge `<TASK-ID>` (full pipeline). Final approval required. Summaries:
|
|
49
|
+
> - Reviewer: <summary>
|
|
50
|
+
> - UI Reviewer: <summary or 'not run'>
|
|
51
|
+
> - QA: <summary with results>
|
|
52
|
+
> Confirm merge? (y/N)"
|
|
26
53
|
|
|
27
|
-
|
|
54
|
+
3B.3. On explicit `y`:
|
|
55
|
+
- `cloverleaf-cli emit-gate-decision <repo_root> <TASK-ID> final_approval_gate approve human`
|
|
56
|
+
- `cloverleaf-cli advance-status <repo_root> <TASK-ID> merged human final_approval_gate full_pipeline`
|
|
57
|
+
- Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> merged (full pipeline)"`.
|
|
28
58
|
|
|
29
|
-
4.
|
|
30
|
-
- Emit the gate decision: `cloverleaf-cli emit-gate-decision <repo_root> <TASK-ID> human_merge approve human`. Note: decision is `approve` (not `approved` — per the gate-decision schema's enum).
|
|
31
|
-
- Advance: `cloverleaf-cli advance-status <repo_root> <TASK-ID> merged human human_merge fast_lane`.
|
|
32
|
-
- Commit state: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> merged"`.
|
|
59
|
+
### 4. Common: report
|
|
33
60
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
Report:
|
|
62
|
+
- "✓ Merged `<TASK-ID>`. Branch `cloverleaf/<TASK-ID>` is ready for you to push and open a PR."
|
|
63
|
+
- "Suggested: `git push origin cloverleaf/<TASK-ID>` then open a PR against `main`."
|
|
37
64
|
|
|
38
65
|
## Rules
|
|
39
66
|
|
|
40
|
-
-
|
|
41
|
-
- The
|
|
67
|
+
- Only proceed on explicit `y`, `Y`, `yes`, `YES`. Anything else: abort without state change.
|
|
68
|
+
- The skill does NOT push the branch or open a PR.
|
|
69
|
+
- Fast lane and full pipeline use distinct gates — the state machine records which path was taken.
|
|
42
70
|
- If the user declines, no state change and no commit.
|
|
@@ -53,4 +53,12 @@ The user has invoked this skill with a brief. Your job: turn the brief into a st
|
|
|
53
53
|
## Rules
|
|
54
54
|
|
|
55
55
|
- Do not guess at acceptance criteria. If the brief is too vague (e.g., "make it faster" with no target), ask the user a clarifying question before writing the file.
|
|
56
|
-
-
|
|
56
|
+
- **risk_class inference:** `risk_class` determines the Delivery pipeline (`"low"` → fast lane; `"high"` → full pipeline). Rules:
|
|
57
|
+
1. If the user passed `--risk=high` or `--risk=low` as a flag on the skill invocation, honor it.
|
|
58
|
+
2. Otherwise, set `risk_class: "high"` when the brief OR any acceptance criterion matches (case-insensitive) any of these keywords:
|
|
59
|
+
`site/`, `UI`, `page`, `component`, `style`, `visual`, `layout`, `render`, `display`, `accessibility`, `a11y`, `responsive`, `.astro`, `.css`, `.html`
|
|
60
|
+
3. Also set `risk_class: "high"` for breaking APIs or cross-project work (v0.1.1 behavior, retained).
|
|
61
|
+
4. Default: `risk_class: "low"`.
|
|
62
|
+
- After writing the task, report the chosen risk_class and how it was determined, e.g.:
|
|
63
|
+
> "Risk class: `high` → full pipeline (matched keyword `component` in acceptance criterion). Override with `--risk=low` if desired."
|
|
64
|
+
- Users can manually edit `risk_class` in the task JSON before running `/cloverleaf-run`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloverleaf-qa
|
|
3
|
+
description: Run the QA agent on a task in the `qa` state (full pipeline only). Dispatches a subagent to run per-package test suites against an isolated worktree; emits feedback envelope with results; advances qa → final-gate on pass or loops back to implementing on bounce. Usage — /cloverleaf-qa <TASK-ID>.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloverleaf — qa
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
0. Ensure you are on `main`. If not, `git checkout main`. If main has uncommitted changes, stop and report.
|
|
11
|
+
|
|
12
|
+
1. Capture the TASK-ID argument.
|
|
13
|
+
|
|
14
|
+
2. Load the task:
|
|
15
|
+
```
|
|
16
|
+
cloverleaf-cli load-task <repo_root> <TASK-ID>
|
|
17
|
+
```
|
|
18
|
+
Verify `status === "qa"`. If not, report and stop.
|
|
19
|
+
|
|
20
|
+
3. Confirm feature branch exists: `git rev-parse --verify cloverleaf/<TASK-ID>`.
|
|
21
|
+
|
|
22
|
+
4. Load QA rules JSON:
|
|
23
|
+
```bash
|
|
24
|
+
cat ~/.claude/plugins/cloverleaf/config/qa-rules.json
|
|
25
|
+
```
|
|
26
|
+
Capture for the subagent as `qa_rules`.
|
|
27
|
+
|
|
28
|
+
5. Compute diff:
|
|
29
|
+
```bash
|
|
30
|
+
git diff main..cloverleaf/<TASK-ID>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
6. Dispatch the QA subagent via the Task tool:
|
|
34
|
+
- `subagent_type`: `general-purpose`
|
|
35
|
+
- `model`: `sonnet`
|
|
36
|
+
- Prompt: contents of `~/.claude/plugins/cloverleaf/prompts/qa.md` with substitutions for `{{task}}`, `{{diff}}`, `{{branch}}`, `{{base_branch}}`, `{{repo_root}}`, `{{qa_rules}}` (the JSON loaded in step 4).
|
|
37
|
+
|
|
38
|
+
7. Parse response: expect `{"verdict": "pass"|"bounce"|"escalate", "summary", "findings", "results"}`.
|
|
39
|
+
|
|
40
|
+
8. Branch on verdict:
|
|
41
|
+
|
|
42
|
+
**Pass:**
|
|
43
|
+
```
|
|
44
|
+
cloverleaf-cli advance-status <repo_root> <TASK-ID> final-gate agent --path=full_pipeline
|
|
45
|
+
```
|
|
46
|
+
Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> qa passed → final-gate"`.
|
|
47
|
+
Report: "✓ QA passed (`<passed>/<total>` tests). State → final-gate. Next: `/cloverleaf-merge <TASK-ID>`."
|
|
48
|
+
|
|
49
|
+
**Bounce:**
|
|
50
|
+
1. Write feedback envelope: `echo '<json>' > /tmp/cloverleaf-fb-q.json`
|
|
51
|
+
2. `cloverleaf-cli write-feedback <repo_root> <TASK-ID> /tmp/cloverleaf-fb-q.json --prefix=q`
|
|
52
|
+
3. `cloverleaf-cli advance-status <repo_root> <TASK-ID> implementing agent --path=full_pipeline`
|
|
53
|
+
4. Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> qa bounced → implementing"`.
|
|
54
|
+
5. Report: "✗ QA bounced. `<failed>/<total>` tests failed. State → implementing. Next: `/cloverleaf-implement <TASK-ID>`."
|
|
55
|
+
|
|
56
|
+
**Escalate:**
|
|
57
|
+
1. `cloverleaf-cli advance-status <repo_root> <TASK-ID> escalated agent`
|
|
58
|
+
2. Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> qa escalated"`.
|
|
59
|
+
3. Report: "✗ QA escalated. Review infrastructure and retry manually."
|
|
60
|
+
|
|
61
|
+
## Rules
|
|
62
|
+
|
|
63
|
+
- Never push. Read-only. Do not modify source.
|
|
64
|
+
- On illegal state transition, report and stop.
|
package/skills/cloverleaf-run.md
CHANGED
|
@@ -1,41 +1,95 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cloverleaf-run
|
|
3
|
-
description: End-to-end orchestrator.
|
|
3
|
+
description: End-to-end orchestrator. Reads task.risk_class to dispatch fast lane (implement → review → merge) or full pipeline (implement → document → review → [ui-review?] → qa → final-merge). Per-agent bounce counters (max 3 each). Usage — /cloverleaf-run <TASK-ID>.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Cloverleaf — run (orchestrator)
|
|
7
7
|
|
|
8
8
|
## Branch discipline
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Each sub-skill runs from `main`. Between steps, confirm branch is `main` before proceeding. All sub-skills return the user to `main`.
|
|
11
|
+
|
|
12
|
+
## Per-agent bounce budget
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
MAX_REVIEWER_BOUNCES = 3
|
|
16
|
+
MAX_UI_REVIEWER_BOUNCES = 3
|
|
17
|
+
MAX_QA_BOUNCES = 3
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
These counters live in-session (not persisted). Rerunning `/cloverleaf-run` resets.
|
|
11
21
|
|
|
12
22
|
## Steps
|
|
13
23
|
|
|
14
|
-
1. Capture
|
|
24
|
+
1. Capture TASK-ID.
|
|
25
|
+
|
|
26
|
+
2. Load task: `cloverleaf-cli load-task <repo_root> <TASK-ID>`. Verify `status === "pending"`. If not, report and stop.
|
|
27
|
+
|
|
28
|
+
3. Read `task.risk_class`:
|
|
29
|
+
- `"low"` → go to section 4 (Fast Lane)
|
|
30
|
+
- `"high"` → go to section 5 (Full Pipeline)
|
|
31
|
+
|
|
32
|
+
### 4. Fast Lane
|
|
33
|
+
|
|
34
|
+
Initialize `reviewer_bounces = 0`.
|
|
35
|
+
|
|
36
|
+
Loop:
|
|
37
|
+
a. Inline `/cloverleaf-implement <TASK-ID>` steps.
|
|
38
|
+
b. Inline `/cloverleaf-review <TASK-ID>` steps.
|
|
39
|
+
c. Reload task. If `status === "automated-gates"`: pass! Break loop.
|
|
40
|
+
d. If `status === "implementing"`: Reviewer bounced. `reviewer_bounces += 1`. If `reviewer_bounces >= MAX_REVIEWER_BOUNCES`, escalate (section 6). Else continue loop.
|
|
41
|
+
e. Else: unexpected state. Report and stop.
|
|
42
|
+
|
|
43
|
+
After loop: inline `/cloverleaf-merge <TASK-ID>`.
|
|
44
|
+
|
|
45
|
+
### 5. Full Pipeline
|
|
46
|
+
|
|
47
|
+
Initialize `reviewer_bounces = 0`, `ui_reviewer_bounces = 0`, `qa_bounces = 0`.
|
|
48
|
+
|
|
49
|
+
5.1. **Implementer → Documenter → Reviewer loop:**
|
|
50
|
+
|
|
51
|
+
Loop:
|
|
52
|
+
a. Inline `/cloverleaf-implement <TASK-ID>` steps.
|
|
53
|
+
b. Inline `/cloverleaf-document <TASK-ID>` steps.
|
|
54
|
+
c. Inline `/cloverleaf-review <TASK-ID>` steps.
|
|
55
|
+
d. Reload task. If `status === "automated-gates"`: pass! Exit this loop.
|
|
56
|
+
e. If `status === "implementing"`: Reviewer bounced. `reviewer_bounces += 1`. If `reviewer_bounces >= MAX_REVIEWER_BOUNCES`, escalate. Else continue loop.
|
|
57
|
+
f. Else: unexpected. Report and stop.
|
|
15
58
|
|
|
16
|
-
2.
|
|
59
|
+
5.2. **UI-path detection and conditional UI Review:**
|
|
17
60
|
|
|
18
|
-
|
|
61
|
+
```bash
|
|
62
|
+
cloverleaf-cli detect-ui-paths <repo_root> <TASK-ID>
|
|
63
|
+
```
|
|
19
64
|
|
|
20
|
-
|
|
65
|
+
If output is `true`:
|
|
66
|
+
- Advance: `cloverleaf-cli advance-status <repo_root> <TASK-ID> ui-review agent --path=full_pipeline`. Commit.
|
|
67
|
+
- UI-review loop:
|
|
68
|
+
a. Inline `/cloverleaf-ui-review <TASK-ID>` steps.
|
|
69
|
+
b. Reload task. If `status === "qa"`: pass! Exit UI-review loop.
|
|
70
|
+
c. If `status === "implementing"`: UI Reviewer bounced. `ui_reviewer_bounces += 1`. If `>= MAX_UI_REVIEWER_BOUNCES`, escalate. Else return to section 5.1 (Implementer re-runs, which then re-documents, re-reviews).
|
|
71
|
+
d. Else: unexpected. Report and stop.
|
|
21
72
|
|
|
22
|
-
|
|
73
|
+
If output is `false`: skip UI review. Advance: `cloverleaf-cli advance-status <repo_root> <TASK-ID> qa agent --path=full_pipeline`. Commit.
|
|
23
74
|
|
|
24
|
-
|
|
75
|
+
5.3. **QA loop:**
|
|
25
76
|
|
|
26
|
-
|
|
77
|
+
Loop:
|
|
78
|
+
a. Inline `/cloverleaf-qa <TASK-ID>` steps.
|
|
79
|
+
b. Reload task. If `status === "final-gate"`: pass! Exit loop.
|
|
80
|
+
c. If `status === "implementing"`: QA bounced. `qa_bounces += 1`. If `qa_bounces >= MAX_QA_BOUNCES`, escalate. Else return to section 5.1.
|
|
81
|
+
d. Else: unexpected. Report and stop.
|
|
27
82
|
|
|
28
|
-
|
|
83
|
+
5.4. **Final merge:** Inline `/cloverleaf-merge <TASK-ID>` steps (branches to full-pipeline gate per state).
|
|
29
84
|
|
|
30
|
-
|
|
85
|
+
### 6. Escalation
|
|
31
86
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- Report: "✗ Escalated `<TASK-ID>` after 3 bounces. Review the feedback files under `.cloverleaf/feedback/` and either refine the task or take over manually."
|
|
87
|
+
- `cloverleaf-cli advance-status <repo_root> <TASK-ID> escalated agent`
|
|
88
|
+
- Commit: `git add .cloverleaf/ && git commit -m "cloverleaf: <TASK-ID> escalated (bounce budget exhausted)"`.
|
|
89
|
+
- Report: "✗ Escalated `<TASK-ID>`. Review `.cloverleaf/feedback/` and either refine the task or take over manually. Counters: reviewer=<N>, ui_reviewer=<N>, qa=<N>."
|
|
36
90
|
|
|
37
91
|
## Rules
|
|
38
92
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
93
|
+
- Each agent has its own 3-bounce budget. Bounces from different agents do NOT share counters.
|
|
94
|
+
- On any sub-skill error or escalation, orchestrator stops with clear message.
|
|
95
|
+
- Human merge gate is NOT skipped; confirmation is still required at merge time.
|