@cloverleaf/reference-impl 0.4.0 → 0.5.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/plugin.json +1 -1
- package/VERSION +1 -1
- package/config/discovery.json +5 -0
- package/config/ui-review.json +2 -1
- package/dist/axe-dedupe.mjs +6 -2
- package/dist/cli.mjs +137 -4
- package/dist/discovery-config.mjs +26 -0
- package/dist/feedback.mjs +1 -1
- package/dist/ids.mjs +26 -1
- package/dist/index.mjs +1 -1
- package/dist/plan.mjs +115 -0
- package/dist/plugin-path.mjs +19 -0
- package/dist/rfc.mjs +38 -0
- package/dist/spike.mjs +37 -0
- package/dist/task.mjs +57 -0
- package/dist/ui-review-config.mjs +5 -1
- package/dist/work-item.mjs +49 -0
- package/lib/axe-dedupe.ts +13 -2
- package/lib/cli.ts +135 -4
- package/lib/discovery-config.ts +35 -0
- package/lib/feedback.ts +1 -1
- package/lib/ids.ts +25 -1
- package/lib/index.ts +1 -1
- package/lib/plan.ts +147 -0
- package/lib/plugin-path.ts +21 -0
- package/lib/rfc.ts +62 -0
- package/lib/spike.ts +60 -0
- package/lib/task.ts +90 -0
- package/lib/ui-review-config.ts +6 -1
- package/lib/work-item.ts +78 -0
- package/package.json +1 -1
- package/prompts/plan.md +63 -0
- package/prompts/researcher.md +74 -0
- package/prompts/ui-reviewer.md +19 -6
- package/skills/cloverleaf-breakdown/SKILL.md +74 -0
- package/skills/cloverleaf-discover/SKILL.md +139 -0
- package/skills/cloverleaf-document/SKILL.md +2 -2
- package/skills/cloverleaf-draft-rfc/SKILL.md +99 -0
- package/skills/cloverleaf-gate/SKILL.md +106 -0
- package/skills/cloverleaf-implement/SKILL.md +3 -3
- package/skills/cloverleaf-merge/SKILL.md +26 -5
- package/skills/cloverleaf-new-rfc/SKILL.md +76 -0
- package/skills/cloverleaf-new-task/SKILL.md +2 -2
- package/skills/cloverleaf-qa/SKILL.md +16 -6
- package/skills/cloverleaf-review/SKILL.md +18 -8
- package/skills/cloverleaf-spike/SKILL.md +66 -0
- package/skills/cloverleaf-ui-review/SKILL.md +17 -7
- package/lib/state.ts +0 -137
package/lib/work-item.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { emitStatusTransition, formatReason } from './events.js';
|
|
5
|
+
import { validateStatusTransitionLegality } from '@cloverleaf/standard/validators/index.js';
|
|
6
|
+
import type { StatusTransitions } from '@cloverleaf/standard/validators/index.js';
|
|
7
|
+
|
|
8
|
+
const req = createRequire(import.meta.url);
|
|
9
|
+
|
|
10
|
+
export function loadStateMachine(type: 'task' | 'rfc' | 'spike' | 'plan'): StatusTransitions {
|
|
11
|
+
const pkgPath = req.resolve('@cloverleaf/standard/package.json');
|
|
12
|
+
const pkgDir = pkgPath.replace(/\/package\.json$/, '');
|
|
13
|
+
return JSON.parse(readFileSync(`${pkgDir}/state-machines/${type}.json`, 'utf-8')) as StatusTransitions;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AdvanceWorkItemParams<T> {
|
|
17
|
+
repoRoot: string;
|
|
18
|
+
workItemType: 'task' | 'rfc' | 'spike' | 'plan';
|
|
19
|
+
project: string;
|
|
20
|
+
id: string;
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
actor: 'agent' | 'human';
|
|
24
|
+
stateMachine: StatusTransitions;
|
|
25
|
+
validateFixture: Record<string, unknown>;
|
|
26
|
+
save: (proposed: T & { status: string }) => void;
|
|
27
|
+
proposed: T;
|
|
28
|
+
gate?: string;
|
|
29
|
+
path?: 'fast_lane' | 'full_pipeline';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AdvanceWorkItemResult {
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function advanceWorkItemStatus<T>(params: AdvanceWorkItemParams<T>): AdvanceWorkItemResult {
|
|
38
|
+
const { repoRoot, workItemType, project, id, from, to, actor, stateMachine, validateFixture, save, gate, path } = params;
|
|
39
|
+
|
|
40
|
+
const reason = formatReason({ gate, path });
|
|
41
|
+
const event = {
|
|
42
|
+
event_id: randomUUID(),
|
|
43
|
+
event_type: 'status_transition' as const,
|
|
44
|
+
occurred_at: new Date().toISOString(),
|
|
45
|
+
work_item_id: { project, id },
|
|
46
|
+
work_item_type: workItemType,
|
|
47
|
+
from_status: from,
|
|
48
|
+
to_status: to,
|
|
49
|
+
actor: { kind: actor, id: actor },
|
|
50
|
+
...(reason ? { reason } : {}),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const result = validateStatusTransitionLegality(event, stateMachine, validateFixture as never);
|
|
54
|
+
if (!result.ok) {
|
|
55
|
+
const msgs = result.violations.map((v) => v.message).join('; ');
|
|
56
|
+
throw new Error(`Illegal transition ${from} → ${to}: ${msgs}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const emittedPath = emitStatusTransition(repoRoot, {
|
|
60
|
+
project,
|
|
61
|
+
workItemType,
|
|
62
|
+
workItemId: id,
|
|
63
|
+
from,
|
|
64
|
+
to,
|
|
65
|
+
actor,
|
|
66
|
+
gate,
|
|
67
|
+
path,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
save(params.proposed as T & { status: string });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const inner = err instanceof Error ? err.message : String(err);
|
|
74
|
+
throw new Error(`orphan event written to ${emittedPath} but ${workItemType} save failed: ${inner}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { from, to };
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloverleaf/reference-impl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Reference implementation of the Cloverleaf methodology as Claude Code skills. Implements the Tight Loop (Implementer + Reviewer).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/prompts/plan.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Plan Agent
|
|
2
|
+
|
|
3
|
+
**Operation:** breakdown
|
|
4
|
+
|
|
5
|
+
**Repo root:** {{repo_root}}
|
|
6
|
+
**Doc context URI:** {{doc_context_uri}}
|
|
7
|
+
|
|
8
|
+
You are the Plan Agent. Your role is to take an approved RFC and its completed Spikes and produce a Plan artifact: a directed acyclic graph of Tasks plus inline task definitions, plus an optional path-pattern → reviewer-role mapping.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
**RFC:** {{rfc}}
|
|
13
|
+
**Completed spikes:** {{spikes}}
|
|
14
|
+
**Path rules (optional):** {{path_rules}}
|
|
15
|
+
|
|
16
|
+
## Your task
|
|
17
|
+
|
|
18
|
+
1. Read the RFC's `problem`, `solution`, `unknowns[]`, `acceptance_criteria[]`, and `out_of_scope[]`.
|
|
19
|
+
2. Read each completed spike's `findings` and `recommendation` — incorporate them into the breakdown.
|
|
20
|
+
3. Decompose the work into 3–8 tasks. Each task must:
|
|
21
|
+
- Be independently testable and reviewable.
|
|
22
|
+
- Have a clear `definition_of_done` (measurable, not aspirational).
|
|
23
|
+
- Have explicit `acceptance_criteria` (what "done" looks like from the outside).
|
|
24
|
+
- Carry a `risk_class`: `"low"` for trivial / doc-only tasks (fast-lane Delivery), `"high"` otherwise (full-pipeline Delivery).
|
|
25
|
+
- Start at `status: "pending"`. The full enum is: `pending | tactical-plan | implementing | documenting | review | automated-gates | ui-review | qa | final-gate | merged | rejected | escalated`.
|
|
26
|
+
Note: "pending" is the correct initial value — the string "todo" is invalid and will be rejected by schema validation.
|
|
27
|
+
4. Build a `task_dag` using the edge-based shape from `dependency-dag.schema.json`:
|
|
28
|
+
- `nodes: Array<{project, id}>` — one workItemRef per task in `tasks[]`.
|
|
29
|
+
- `edges: Array<{from: {project, id}, to: {project, id}}>` — directed edges from prerequisite to dependent. `to` cannot start until `from` completes. Both endpoints must appear in `nodes`. DAG roots are nodes that appear in no edge's `to` field.
|
|
30
|
+
5. If `path_rules` is non-empty, emit `path_reviewer_map: Array<{pattern, role}>` by mapping the rules' path globs to reviewer roles.
|
|
31
|
+
|
|
32
|
+
## Emit a Plan JSON conforming to `plan.schema.json`
|
|
33
|
+
|
|
34
|
+
Required top-level fields:
|
|
35
|
+
- `type: "plan"`
|
|
36
|
+
- `id` — supplied by orchestrator (the next available ID in the project's work-item namespace)
|
|
37
|
+
- `project` — supplied by orchestrator
|
|
38
|
+
- `status: "drafting"`
|
|
39
|
+
- `owner` — `{ "kind": "agent", "id": "plan" }`
|
|
40
|
+
- `parent_rfc: { project: <rfc.project>, id: <rfc.id> }`
|
|
41
|
+
- `task_dag` — as described above
|
|
42
|
+
- `tasks` — array of full Task docs conforming to `task.schema.json`
|
|
43
|
+
|
|
44
|
+
### Critical runtime invariants
|
|
45
|
+
- The set of `tasks[].id` MUST equal the set of workItemRefs in `task_dag.nodes`. No task without a DAG node; no DAG node without a task.
|
|
46
|
+
- `task_dag.edges` MUST form an acyclic graph. Cycles will be rejected by the orchestrator at materialisation time.
|
|
47
|
+
- `additionalProperties: false` on Plan and on each Task — emit ONLY schema-defined fields.
|
|
48
|
+
|
|
49
|
+
### Important: the Plan has NO `title` field
|
|
50
|
+
Unlike RFC / Spike / Task, the Plan schema does not define a `title` field. Do not add one.
|
|
51
|
+
|
|
52
|
+
## Task IDs
|
|
53
|
+
- Each task's `id` must follow the project's ID convention (typically `<PROJECT>-<N>`).
|
|
54
|
+
- Task IDs must not collide with any existing `.cloverleaf/{rfcs,spikes,plans,tasks}/` work item. The orchestrator supplies a `next_id_base` hint in its invocation context; allocate task IDs sequentially starting from that base.
|
|
55
|
+
- Example: if `next_id_base` is `CLV-13` and you're emitting 3 tasks, use `CLV-13`, `CLV-14`, `CLV-15` in `tasks[]` and in `task_dag.nodes`.
|
|
56
|
+
|
|
57
|
+
## Output format
|
|
58
|
+
Write the Plan JSON to stdout, nothing else. No prose, no markdown fences. The orchestrator captures stdout and validates against `plan.schema.json` (and each task via `task.schema.json`). Budget: 3 bounces per invocation.
|
|
59
|
+
|
|
60
|
+
## General rules
|
|
61
|
+
- **Schema compliance is mandatory.** Each task in `tasks[]` must independently pass `task.schema.json` validation.
|
|
62
|
+
- **No file writes.** Orchestrator persists your output.
|
|
63
|
+
- Gate for Plan approval: `task_batch_gate` (approved by human after `gate-pending`).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Researcher Agent
|
|
2
|
+
|
|
3
|
+
**Operation:** {{operation}} (`draftRfc` or `runSpike`)
|
|
4
|
+
|
|
5
|
+
**Repo root:** {{repo_root}}
|
|
6
|
+
**Doc context URI:** {{doc_context_uri}}
|
|
7
|
+
|
|
8
|
+
You are the Researcher Agent. Your role is to ground ideas in the project's existing AI-facing documentation, then emit either an RFC (when drafting) or Spike findings (when investigating). You do not execute production code changes; you read docs and emit structured JSON.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## If operation = draftRfc
|
|
13
|
+
|
|
14
|
+
**Brief:** {{brief}}
|
|
15
|
+
|
|
16
|
+
**Prior RFC (if this is a re-draft after spikes):** {{prior_rfc}}
|
|
17
|
+
**Completed spikes (if any):** {{completed_spikes}}
|
|
18
|
+
|
|
19
|
+
### Your task
|
|
20
|
+
1. Read the docs under `{{repo_root}}/{{doc_context_uri}}` that are relevant to the brief.
|
|
21
|
+
2. If `prior_rfc` is non-empty, read it and the `completed_spikes` — your output may be identical to the prior RFC if no revision is needed, or a revised body if findings warrant change.
|
|
22
|
+
3. Emit a single JSON document conforming to `rfc.schema.json` (from `@cloverleaf/standard`). Required top-level fields:
|
|
23
|
+
- `type: "rfc"`
|
|
24
|
+
- `id` — supplied by orchestrator via `{{repo_root}}` context; preserve the existing ID if prior_rfc is given, else use the orchestrator's allocated ID
|
|
25
|
+
- `project` — supplied by orchestrator
|
|
26
|
+
- `status: "drafting"`
|
|
27
|
+
- `owner` — object `{ kind: "agent"|"human"|"system", id: string }`; use `{ "kind": "agent", "id": "researcher" }`
|
|
28
|
+
- `title` — short name
|
|
29
|
+
- `problem` — paragraph describing what's wrong or missing
|
|
30
|
+
- `solution` — paragraph describing the proposed approach
|
|
31
|
+
- `unknowns` — **array of strings**, each entry is one uncertainty that should become a Spike (e.g. "What is the webkit install size?"). Empty array if no uncertainties.
|
|
32
|
+
- `acceptance_criteria` — array of strings (minItems: 1), each a measurable condition for RFC approval
|
|
33
|
+
- `out_of_scope` — array of strings; can be empty
|
|
34
|
+
|
|
35
|
+
### Important schema notes
|
|
36
|
+
- **The RFC does NOT contain a `spikes` field.** Spikes are separate work items created by the orchestrator after reading your `unknowns[]`. Each `unknowns[]` entry becomes a Spike's `question`.
|
|
37
|
+
- `additionalProperties: false` — do not emit extra fields. Optional schema fields: `parent`, `relationships`, `extensions`.
|
|
38
|
+
|
|
39
|
+
### Output format
|
|
40
|
+
Write the JSON to stdout, nothing else. No prose, no markdown fences, no explanation. The orchestrator captures stdout and validates against the schema. Budget: 3 bounces per invocation.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## If operation = runSpike
|
|
45
|
+
|
|
46
|
+
**Spike input:** {{spike}}
|
|
47
|
+
|
|
48
|
+
### Your task
|
|
49
|
+
1. Read the spike's `question` and `method` fields.
|
|
50
|
+
2. `method` is one of:
|
|
51
|
+
- `research` — read documentation, synthesise an answer
|
|
52
|
+
- `prototype` — describe a minimal prototype (you don't build it — the Implementer does in Delivery; spike output describes what to prototype)
|
|
53
|
+
- `benchmark` — describe what to measure and expected comparison axes
|
|
54
|
+
3. Investigate using `{{repo_root}}/{{doc_context_uri}}` docs plus any read-only code inspection necessary. Do NOT modify files.
|
|
55
|
+
4. Emit the input spike object with these additions/changes:
|
|
56
|
+
- `status: "completed"`
|
|
57
|
+
- `findings: string` — **a single string** summarising evidence-backed observations. Multi-sentence paragraphs are fine; do NOT emit an array.
|
|
58
|
+
- `recommendation: string` — **a single string** summarising the implication for the RFC / Plan.
|
|
59
|
+
|
|
60
|
+
### Schema compliance
|
|
61
|
+
- Preserve all existing spike fields (`id, type, owner, project, title, parent_rfc, question, method`).
|
|
62
|
+
- Add `findings` and `recommendation`.
|
|
63
|
+
- Output must conform to `spike.schema.json` (from `@cloverleaf/standard`).
|
|
64
|
+
- `additionalProperties: false` — no extra fields.
|
|
65
|
+
|
|
66
|
+
### Output format
|
|
67
|
+
Write the updated spike JSON to stdout, nothing else. Budget: 3 bounces per invocation.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## General rules
|
|
72
|
+
- **Schema compliance is mandatory.** If your output fails AJV validation, you bounce and try again. Budget: 3 bounces per invocation.
|
|
73
|
+
- **No side-effects on the filesystem.** Don't write files. The orchestrator persists your output.
|
|
74
|
+
- **No tool use beyond reading.** v0.5 limits Researcher to doc-reading; web search and external tools are future work.
|
package/prompts/ui-reviewer.md
CHANGED
|
@@ -13,9 +13,21 @@ You are the Cloverleaf UI Reviewer. Your job: review a task's UI changes at mult
|
|
|
13
13
|
- **Affected routes**: {{affected_routes}} — either a JSON array of route paths (e.g., `["/faq/"]`), or the string `"all"`, or `[]`
|
|
14
14
|
- **UI review config**: {{ui_review_config}} — the loaded `UiReviewConfig` object (viewports, visualDiff, axe) as JSON. The `viewports` array contains named entries such as `mobile`, `tablet`, and `desktop` with their respective `{ width, height }` dimensions.
|
|
15
15
|
|
|
16
|
+
## Paths
|
|
17
|
+
|
|
18
|
+
You operate in two filesystem locations — keep them straight:
|
|
19
|
+
|
|
20
|
+
- `<worktree>` — the ephemeral worktree at `$TMPDIR` (set up in step 2 of the Runtime procedure). You run the dev server here and execute Playwright here.
|
|
21
|
+
- `<repoRoot>` — the main repository root at `{{repo_root}}` (always an absolute path). This is the ONLY location where baselines, diff PNGs, candidate PNGs, and artifacts are written.
|
|
22
|
+
|
|
23
|
+
**All `compareVisual` paths MUST be rooted at `{{repo_root}}`, NOT at `$TMPDIR`.**
|
|
24
|
+
|
|
25
|
+
The rationale: baselines on `{{repo_root}}/.cloverleaf/baselines/` get picked up by subsequent `git add` + `git commit` steps in the UI Reviewer, which run on the feature branch. The merge skill (v0.4.1+) then merges those commits to main via `git merge --no-ff`. Writing to the worktree's `.cloverleaf/` would strand the files and `git worktree remove --force` would discard them on teardown.
|
|
26
|
+
|
|
16
27
|
## Scope (v0.4)
|
|
17
28
|
|
|
18
29
|
- **Accessibility (axe-core):** run at the viewports listed in `{{ui_review_config}}.axe.viewports`.
|
|
30
|
+
Apply the allowlist in `{{ui_review_config}}.axe.ignored` to drop pre-existing violations that the consumer has accepted (e.g., a11y debt being tracked separately).
|
|
19
31
|
Dedupe findings across viewports by the `{{ui_review_config}}.axe.dedupeBy` composite key (default `["ruleId", "target"]`).
|
|
20
32
|
Emit one finding per (ruleId, target) pair, with a `metadata.viewports` array aggregating the viewports where the violation was detected.
|
|
21
33
|
- **Visual diff (pixelmatch):** when `{{ui_review_config}}.visualDiff.enabled` is true, screenshot each route at each viewport in `{{ui_review_config}}.viewports`, compare to `.cloverleaf/baselines/{route-slug}-{viewport}.png`, emit `severity: "info"` findings with baseline/candidate/diff attachments when the diff ratio exceeds `maxDiffRatio`.
|
|
@@ -50,7 +62,7 @@ The `PLAYWRIGHT_BROWSERS_PATH` environment variable is set to `~/.cache/ms-playw
|
|
|
50
62
|
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`.
|
|
51
63
|
|
|
52
64
|
5. Determine the site base path:
|
|
53
|
-
1. Check
|
|
65
|
+
1. Check `{{repo_root}}/.cloverleaf/config/astro-base.json`. Expected shape: `{ "base": "<path>" }`. If present, use the `base` field verbatim and skip to step 6. (Consumer override — checked before parsing astro config.)
|
|
54
66
|
2. Otherwise, attempt to locate and parse an astro config file (common locations: `site/astro.config.mjs`, `astro.config.mjs` at repo root, `apps/web/astro.config.mjs`). Best-effort fallback.
|
|
55
67
|
3. If both fail, treat base as empty string.
|
|
56
68
|
|
|
@@ -61,11 +73,12 @@ The `PLAYWRIGHT_BROWSERS_PATH` environment variable is set to `~/.cache/ms-playw
|
|
|
61
73
|
- Navigate to `http://localhost:{{preview_port}}<base><route>`. If 404, retry without the base.
|
|
62
74
|
- `page.screenshot({ fullPage: false })` → candidate PNG buffer.
|
|
63
75
|
- Compute slug for the route (lowercase, strip leading/trailing slashes, replace slashes with hyphens; `/` → `index`).
|
|
76
|
+
- Note: use `{{repo_root}}` (the absolute main-repo path), NOT `$TMPDIR` or the worktree. See the "Paths" section.
|
|
64
77
|
- Call `compareVisual` (from `lib/visual-diff.ts`) with:
|
|
65
|
-
- `baselinePath =
|
|
78
|
+
- `baselinePath = {{repo_root}}/.cloverleaf/baselines/{slug}-{viewport}.png`
|
|
66
79
|
- `candidateBuf = <candidate PNG>`
|
|
67
|
-
- `diffPath =
|
|
68
|
-
- `candidateOutPath =
|
|
80
|
+
- `diffPath = {{repo_root}}/.cloverleaf/runs/{taskId}/ui-review/diff-{slug}-{viewport}.png`
|
|
81
|
+
- `candidateOutPath = {{repo_root}}/.cloverleaf/runs/{taskId}/ui-review/candidate-{slug}-{viewport}.png`
|
|
69
82
|
- `threshold = visualDiff.threshold`
|
|
70
83
|
- `maxDiffRatio = visualDiff.maxDiffRatio`
|
|
71
84
|
- Map result to a finding:
|
|
@@ -86,7 +99,7 @@ The `PLAYWRIGHT_BROWSERS_PATH` environment variable is set to `~/.cache/ms-playw
|
|
|
86
99
|
```
|
|
87
100
|
- Collect each violation as a raw tuple: `{ viewport, ruleId, target, impact, message, helpUrl }` (from `axe.run` output).
|
|
88
101
|
|
|
89
|
-
8. Dedupe raw axe findings via `dedupeAxeFindings(raws, {{ui_review_config}}.axe.dedupeBy)` (from `lib/axe-dedupe.ts`). Emit the returned `Finding[]`.
|
|
102
|
+
8. Dedupe raw axe findings via `dedupeAxeFindings(raws, {{ui_review_config}}.axe.dedupeBy, {{ui_review_config}}.axe.ignored)` (from `lib/axe-dedupe.ts`). The `ignored` parameter drops any finding whose `(ruleId, target)` exactly matches an allowlist entry BEFORE dedupe/grouping. Emit the returned `Finding[]`.
|
|
90
103
|
|
|
91
104
|
9. Severity mapping (preserved from v0.3 via `dedupeAxeFindings`):
|
|
92
105
|
- axe `impact: "critical"` → `severity: "blocker"`
|
|
@@ -109,7 +122,7 @@ The `PLAYWRIGHT_BROWSERS_PATH` environment variable is set to `~/.cache/ms-playw
|
|
|
109
122
|
## Tool constraints
|
|
110
123
|
|
|
111
124
|
- Read-only for source files and tests.
|
|
112
|
-
- You MAY write under
|
|
125
|
+
- You MAY write under `{{repo_root}}/.cloverleaf/baselines/` and `{{repo_root}}/.cloverleaf/runs/{taskId}/ui-review/` on the feature branch — these are the baselines and artifacts.
|
|
113
126
|
- Use `git worktree`: do NOT `git checkout` in the main working directory.
|
|
114
127
|
- Always teardown the server and worktree, even on error.
|
|
115
128
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloverleaf-breakdown
|
|
3
|
+
description: Invoke the Plan agent (operation=breakdown) to decompose an approved RFC + completed spikes into a Plan with task_dag + inline tasks. Transitions Plan drafting → gate-pending (task_batch_gate). Usage — /cloverleaf-breakdown <RFC-ID>.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloverleaf — Plan breakdown
|
|
7
|
+
|
|
8
|
+
The user has invoked this skill with an RFC-ID (e.g., `CLV-009`).
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. Capture `<RFC-ID>` as `$RFC_ID`. If missing, report usage and stop.
|
|
13
|
+
|
|
14
|
+
2. Load and verify the RFC is in status `approved`:
|
|
15
|
+
```
|
|
16
|
+
cloverleaf-cli load-rfc <repo_root> <RFC-ID>
|
|
17
|
+
```
|
|
18
|
+
If `status !== "approved"`, report "RFC must be approved before breakdown" and stop.
|
|
19
|
+
|
|
20
|
+
3. Collect all completed Spikes for this RFC. For each `.cloverleaf/spikes/*.json`:
|
|
21
|
+
- Parse the JSON.
|
|
22
|
+
- If `parent_rfc.id === $RFC_ID` AND `parent_rfc.project === <rfc.project>` AND `status === "completed"`: include in the SPIKES array.
|
|
23
|
+
- If zero matches, SPIKES is `[]`.
|
|
24
|
+
|
|
25
|
+
4. Load discovery config:
|
|
26
|
+
```bash
|
|
27
|
+
CFG=$(cloverleaf-cli discovery-config --repo-root <repo_root>)
|
|
28
|
+
DOC_CTX=$(echo "$CFG" | jq -r .docContextUri)
|
|
29
|
+
PROJECT_ID=$(echo "$CFG" | jq -r .projectId)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
5. Compute the next work-item ID (this is the Plan's ID):
|
|
33
|
+
```
|
|
34
|
+
PLAN_ID=$(cloverleaf-cli next-work-item-id <repo_root> $PROJECT_ID)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
6. Dispatch the Plan subagent via the Task tool:
|
|
38
|
+
- `subagent_type`: `general-purpose`
|
|
39
|
+
- `model`: `sonnet`
|
|
40
|
+
- Prompt: contents of `$(cloverleaf-cli plugin-root)/prompts/plan.md`, with placeholders:
|
|
41
|
+
- `{{rfc}}` → the full RFC JSON
|
|
42
|
+
- `{{spikes}}` → the SPIKES JSON array
|
|
43
|
+
- `{{doc_context_uri}}` → `$DOC_CTX`
|
|
44
|
+
- `{{repo_root}}` → absolute path to the current repo
|
|
45
|
+
- `{{path_rules}}` → `null` (v0.5 does not auto-populate path_rules; defer to v0.6)
|
|
46
|
+
|
|
47
|
+
In the subagent context, also supply a hint that `next_id_base === $PLAN_ID`, so task IDs in `tasks[]` start at `PLAN_ID + 1`. The Plan agent allocates its own ID as `next_id_base` and task IDs sequentially after.
|
|
48
|
+
|
|
49
|
+
7. Parse subagent response — expected JSON conforming to `plan.schema.json`. Required fields: `id, type: "plan", status: "drafting", owner, project, parent_rfc, task_dag (edge-based), tasks (inline Task docs, status=pending)`. 3-bounce budget per invocation.
|
|
50
|
+
|
|
51
|
+
8. Ensure output `plan.id === $PLAN_ID`, `project === $PROJECT_ID`, `parent_rfc === { project: <rfc.project>, id: $RFC_ID }`. If the subagent drifted, override these before save.
|
|
52
|
+
|
|
53
|
+
9. Save the Plan:
|
|
54
|
+
```
|
|
55
|
+
cloverleaf-cli save-plan <repo_root> /tmp/plan-$PLAN_ID.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
10. Transition drafting → gate-pending:
|
|
59
|
+
```
|
|
60
|
+
cloverleaf-cli advance-plan <repo_root> $PLAN_ID gate-pending agent task_batch_gate
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
11. Commit:
|
|
64
|
+
```bash
|
|
65
|
+
git add .cloverleaf/plans/ .cloverleaf/events/
|
|
66
|
+
git commit -m "cloverleaf: plan $PLAN_ID for RFC $RFC_ID → gate-pending"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
12. Print the Plan ID.
|
|
70
|
+
|
|
71
|
+
## Notes
|
|
72
|
+
|
|
73
|
+
- Tasks from `plan.tasks[]` are NOT yet materialised on disk. Materialisation happens only after a human approves via `/cloverleaf-gate $PLAN_ID approve`, which transitions the Plan to `approved`, at which point the orchestrator runs `cloverleaf-cli materialise-tasks`.
|
|
74
|
+
- If the RFC has no completed spikes (`unknowns[]` was empty), SPIKES is `[]` and the Plan agent works from the RFC alone.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloverleaf-discover
|
|
3
|
+
description: End-to-end Discovery orchestrator. Drives RFC → (Spikes) → Plan → gates → task materialisation. Blocks on human gate decisions (rfc_strategy_gate, task_batch_gate). On Plan approval, materialises tasks and asks whether to kick off /cloverleaf-run on the first DAG root. Usage — /cloverleaf-discover <brief-file>.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloverleaf — discover (Discovery orchestrator)
|
|
7
|
+
|
|
8
|
+
The user has invoked this skill with a brief file path (e.g., `docs/briefs/cross-browser-ui-review.md`).
|
|
9
|
+
|
|
10
|
+
## Branch discipline
|
|
11
|
+
|
|
12
|
+
Each sub-skill runs from `main`. Between steps, confirm branch is `main` before proceeding. All sub-skills return the user to `main`.
|
|
13
|
+
|
|
14
|
+
## Per-agent bounce budget (in-session)
|
|
15
|
+
|
|
16
|
+
- **Researcher and Plan agent bounces are budgeted inside the sub-skills** (`/cloverleaf-draft-rfc`, `/cloverleaf-spike`, `/cloverleaf-breakdown`), each with a 3-bounce per-invocation budget. The orchestrator invokes each sub-skill once per step — if a sub-skill exhausts its budget, it halts, and the orchestrator treats that as a hard halt and dumps state.
|
|
17
|
+
- **Revise loops are budgeted in the orchestrator:**
|
|
18
|
+
```
|
|
19
|
+
MAX_REVISE_LOOPS = 3
|
|
20
|
+
```
|
|
21
|
+
Resets on each `/cloverleaf-discover` invocation.
|
|
22
|
+
|
|
23
|
+
## Steps
|
|
24
|
+
|
|
25
|
+
1. Capture `<brief-file>` as `$BRIEF_FILE`. Verify file exists:
|
|
26
|
+
```bash
|
|
27
|
+
[ -f "$BRIEF_FILE" ] || { echo "Brief file not found: $BRIEF_FILE" >&2; exit 1; }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Verify branch is `main`:
|
|
31
|
+
```bash
|
|
32
|
+
[ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] || { echo "Run /cloverleaf-discover from main" >&2; exit 1; }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. **Create the RFC** — inline `/cloverleaf-new-rfc $BRIEF_FILE` steps. Capture the printed RFC ID as `$RFC_ID`.
|
|
36
|
+
|
|
37
|
+
Initialise revise loop counter:
|
|
38
|
+
```
|
|
39
|
+
revise_loops = 0
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
4. **Draft RFC (Researcher draftRfc)** — inline `/cloverleaf-draft-rfc $RFC_ID` steps. Researcher runs with a per-invocation bounce budget (3 bounces inside draft-rfc).
|
|
43
|
+
- On bounce exhaustion (draft-rfc exits non-zero): dump state to `.cloverleaf/runs/$RFC_ID/discover-crash.json` and halt.
|
|
44
|
+
- Reload the RFC. Its new status is either `spike-in-flight` (unknowns non-empty) or `planning` (unknowns empty).
|
|
45
|
+
|
|
46
|
+
5. **Conditional on RFC status after step 4:**
|
|
47
|
+
- If `planning`: skip the spike loop below; proceed to step 6.
|
|
48
|
+
- If `spike-in-flight`: run every pending spike linked to this RFC:
|
|
49
|
+
```bash
|
|
50
|
+
for each spike in .cloverleaf/spikes/*.json where parent_rfc.id === $RFC_ID AND status === "pending":
|
|
51
|
+
inline /cloverleaf-spike <SPIKE_ID> steps.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If `/cloverleaf-spike` exits non-zero or leaves the spike in a non-`completed` status after returning, treat that as bounce exhaustion: dump state to `.cloverleaf/runs/$RFC_ID/discover-crash.json` (including the spike ID) and halt. The orchestrator does NOT resume partially-run spike trees — if any spike is unresolved, the whole Discovery halts.
|
|
55
|
+
|
|
56
|
+
After all spikes complete:
|
|
57
|
+
|
|
58
|
+
1. Transition RFC spike-in-flight → drafting:
|
|
59
|
+
```
|
|
60
|
+
cloverleaf-cli advance-rfc <repo_root> $RFC_ID drafting agent
|
|
61
|
+
```
|
|
62
|
+
2. Re-invoke `/cloverleaf-draft-rfc $RFC_ID` — Researcher sees the completed spikes and may revise the RFC body. draft-rfc itself re-transitions the RFC to `spike-in-flight` (if new unknowns emerge — should not happen the second time since spikes were answered) or `planning` (expected path).
|
|
63
|
+
|
|
64
|
+
Rationale: redundant redraft is cheap; avoids introducing an off-contract "rfc_revision_required" boolean. The human gate at step 6 catches any missed revisions.
|
|
65
|
+
|
|
66
|
+
6. **Gate: rfc_strategy_gate** — transition RFC to gate-pending:
|
|
67
|
+
```
|
|
68
|
+
cloverleaf-cli advance-rfc <repo_root> $RFC_ID gate-pending agent rfc_strategy_gate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Prompt human (blocking readline):
|
|
72
|
+
```
|
|
73
|
+
RFC $RFC_ID at rfc_strategy_gate: approve / reject / revise [reason]? > _
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Parse input. On:
|
|
77
|
+
- `approve` → inline `/cloverleaf-gate $RFC_ID approve` steps. Continue to step 7.
|
|
78
|
+
- `reject [reason]` → inline `/cloverleaf-gate $RFC_ID reject [reason]` steps. Exit with summary.
|
|
79
|
+
- `revise [reason]` → inline `/cloverleaf-gate $RFC_ID revise [reason]` steps. `revise_loops += 1`. If `revise_loops >= MAX_REVISE_LOOPS`, dump state and halt. Else loop back to step 4.
|
|
80
|
+
|
|
81
|
+
7. **Plan breakdown** — inline `/cloverleaf-breakdown $RFC_ID` steps. Per-invocation bounce budget (3 bounces inside breakdown). Capture `$PLAN_ID`. On bounce exhaustion: dump state and halt.
|
|
82
|
+
|
|
83
|
+
8. **Gate: task_batch_gate** — Plan is already in `gate-pending` (set by breakdown). Prompt:
|
|
84
|
+
```
|
|
85
|
+
PLN $PLAN_ID at task_batch_gate: approve / reject [reason]? > _
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
On:
|
|
89
|
+
- `approve` → inline `/cloverleaf-gate $PLAN_ID approve` steps. Continue.
|
|
90
|
+
- `reject [reason]` → inline `/cloverleaf-gate $PLAN_ID reject [reason]` steps. Exit with summary.
|
|
91
|
+
|
|
92
|
+
9. **Materialise tasks**:
|
|
93
|
+
```bash
|
|
94
|
+
OUT=$(cloverleaf-cli materialise-tasks <repo_root> $PLAN_ID)
|
|
95
|
+
TASK_IDS=$(echo "$OUT" | jq -r '.task_ids[]')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
On materialise-tasks failure (cycle detected, or AJV validation error):
|
|
99
|
+
- The error message from `cloverleaf-cli` identifies the failing task.
|
|
100
|
+
- `materialiseTasksFromPlan` is atomic: the cycle-check and AJV pre-validation run BEFORE any file write, so no task files were created.
|
|
101
|
+
- The Plan remains in `approved` status (the gate was legitimately approved; the materialisation issue is with the Plan's own task DAG, not the gate decision).
|
|
102
|
+
- Dump state to `.cloverleaf/runs/$PLAN_ID/materialise-crash.json` and halt.
|
|
103
|
+
- Operator investigation typically leads to a new `/cloverleaf-breakdown` run to produce a corrected Plan (the rejected path is via the ORIGINAL gate, not a post-approval state correction).
|
|
104
|
+
|
|
105
|
+
10. **Compute DAG roots**:
|
|
106
|
+
```bash
|
|
107
|
+
PLAN_JSON=$(cloverleaf-cli load-plan <repo_root> $PLAN_ID)
|
|
108
|
+
|
|
109
|
+
# DAG roots = nodes whose id never appears as `to.id` in any edge.
|
|
110
|
+
# v0.5 picks the FIRST root only; multi-root concurrent Delivery is v0.6.
|
|
111
|
+
FIRST_ROOT=$(echo "$PLAN_JSON" | jq -r '
|
|
112
|
+
(.task_dag.edges | map(.to.id)) as $targets
|
|
113
|
+
| [.task_dag.nodes[] | select(.id as $n | ($targets | index($n)) | not) | .id][0]
|
|
114
|
+
')
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
v0.5 simplified rule: pick the FIRST root only (first `nodes[]` entry whose `id` does not appear in any `edges[].to.id`). Defer multi-root walk to v0.6.
|
|
118
|
+
|
|
119
|
+
11. **Prompt to kick off first root** (blocking readline):
|
|
120
|
+
```
|
|
121
|
+
N tasks materialised: $TASK_IDS.
|
|
122
|
+
DAG roots: $FIRST_ROOT.
|
|
123
|
+
Run first root via /cloverleaf-run now? [y/N] > _
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
On `y` or `yes` (case-insensitive): inline `/cloverleaf-run <FIRST_ROOT>` steps. On anything else: exit with summary.
|
|
127
|
+
|
|
128
|
+
12. **Exit summary**:
|
|
129
|
+
- RFC ID + final status
|
|
130
|
+
- Spike IDs + findings summary (if any)
|
|
131
|
+
- Plan ID + task count
|
|
132
|
+
- Materialised task IDs
|
|
133
|
+
- Whether first root was invoked (and its outcome if so)
|
|
134
|
+
|
|
135
|
+
## Notes
|
|
136
|
+
|
|
137
|
+
- v0.5 prompts only for the FIRST DAG root. Multi-root concurrent Delivery is v0.6 scope.
|
|
138
|
+
- All bounces and revise loops halt cleanly with a state dump at `.cloverleaf/runs/<RFC_ID>/discover-crash.json`. No partial work is left in an inconsistent state — the gate_decision events and work-item status are always coherent.
|
|
139
|
+
- The readline prompts are compatible with the user's session-bridging tool for automated dogfooding.
|
|
@@ -21,7 +21,7 @@ description: Run the Documenter agent on a task in the `implementing` state (ful
|
|
|
21
21
|
|
|
22
22
|
2. Load the task:
|
|
23
23
|
```
|
|
24
|
-
|
|
24
|
+
cloverleaf-cli load-task <repo_root> <TASK-ID>
|
|
25
25
|
```
|
|
26
26
|
Verify `status === "implementing"`. Verify `risk_class === "high"`. If either check fails, report and stop.
|
|
27
27
|
|
|
@@ -36,7 +36,7 @@ description: Run the Documenter agent on a task in the `implementing` state (ful
|
|
|
36
36
|
5. Dispatch the Documenter subagent via the Task tool:
|
|
37
37
|
- `subagent_type`: `general-purpose`
|
|
38
38
|
- `model`: `sonnet`
|
|
39
|
-
- Prompt: contents of
|
|
39
|
+
- Prompt: contents of `$(cloverleaf-cli plugin-root)/prompts/documenter.md` with substitutions:
|
|
40
40
|
- `{{task}}` → full task JSON (pretty-printed)
|
|
41
41
|
- `{{diff}}` → diff output
|
|
42
42
|
- `{{branch}}` → `cloverleaf/<TASK-ID>`
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloverleaf-draft-rfc
|
|
3
|
+
description: Invoke the Researcher agent (operation=draftRfc) to populate an RFC body from its brief. Emits zero or more Spike work items from the RFC's unknowns[]. Transitions the RFC drafting → spike-in-flight (if unknowns exist) or drafting → planning (if no unknowns). Usage — /cloverleaf-draft-rfc <RFC-ID>.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloverleaf — draft RFC
|
|
7
|
+
|
|
8
|
+
The user has invoked this skill with an RFC-ID (e.g., `CLV-009`).
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. Capture `<RFC-ID>` as `$RFC_ID`. If missing, report usage and stop.
|
|
13
|
+
|
|
14
|
+
2. Load the RFC:
|
|
15
|
+
```
|
|
16
|
+
cloverleaf-cli load-rfc <repo_root> <RFC-ID>
|
|
17
|
+
```
|
|
18
|
+
Parse the JSON. Verify `status === "drafting"`. If not, report and stop.
|
|
19
|
+
|
|
20
|
+
3. Load the discovery config:
|
|
21
|
+
```bash
|
|
22
|
+
CFG=$(cloverleaf-cli discovery-config --repo-root <repo_root>)
|
|
23
|
+
DOC_CTX=$(echo "$CFG" | jq -r .docContextUri)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
4. Gather re-draft context (optional — used when re-invoked after spikes):
|
|
27
|
+
- `PRIOR_RFC`: the current RFC JSON (for a re-draft, same content)
|
|
28
|
+
- `COMPLETED_SPIKES`: for each spike ref linked to this RFC (via spike's `parent_rfc` field), if `status === "completed"`, include its findings/recommendation. Build a JSON array. If none, use `[]`.
|
|
29
|
+
|
|
30
|
+
Find all spikes for this RFC by scanning `.cloverleaf/spikes/*.json` for files whose `parent_rfc.id === $RFC_ID`. Use jq. If none, the array is empty.
|
|
31
|
+
|
|
32
|
+
5. Dispatch the Researcher subagent via the Task tool:
|
|
33
|
+
- `subagent_type`: `general-purpose`
|
|
34
|
+
- `model`: `sonnet`
|
|
35
|
+
- Prompt: contents of `$(cloverleaf-cli plugin-root)/prompts/researcher.md`, with placeholders substituted:
|
|
36
|
+
- `{{operation}}` → `draftRfc`
|
|
37
|
+
- `{{brief}}` → the RFC's `problem` field (seeded by `/cloverleaf-new-rfc`)
|
|
38
|
+
- `{{doc_context_uri}}` → `$DOC_CTX`
|
|
39
|
+
- `{{repo_root}}` → absolute path to the current repo
|
|
40
|
+
- `{{prior_rfc}}` → `PRIOR_RFC` JSON (or the literal string `null` for first draft)
|
|
41
|
+
- `{{completed_spikes}}` → `COMPLETED_SPIKES` JSON array (or `[]`)
|
|
42
|
+
- `{{spike}}` → (unused for draftRfc; substitute `null`)
|
|
43
|
+
|
|
44
|
+
6. Parse subagent's response: expected JSON conforming to `rfc.schema.json`. Required fields: `id`, `type: "rfc"`, `status: "drafting"`, `owner`, `project`, `title`, `problem`, `solution`, `unknowns` (array of strings), `acceptance_criteria`, `out_of_scope`.
|
|
45
|
+
|
|
46
|
+
If output fails schema validation: bounce. Budget: 3 bounces per invocation. On budget exhaustion: report and stop without advancing state.
|
|
47
|
+
|
|
48
|
+
7. Ensure output `id === $RFC_ID` and `project === <original>`. If the subagent changed them, override back.
|
|
49
|
+
|
|
50
|
+
8. Save the populated RFC:
|
|
51
|
+
```bash
|
|
52
|
+
cloverleaf-cli save-rfc <repo_root> /tmp/rfc-draft-$RFC_ID.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
9. Inspect `rfc.unknowns[]`:
|
|
56
|
+
|
|
57
|
+
**If `unknowns.length > 0`:** create one Spike per unknown:
|
|
58
|
+
```bash
|
|
59
|
+
for unknown in rfc.unknowns:
|
|
60
|
+
SPIKE_ID=$(cloverleaf-cli next-work-item-id <repo_root> <project>)
|
|
61
|
+
cat > /tmp/spike-$SPIKE_ID.json <<EOF
|
|
62
|
+
{
|
|
63
|
+
"type": "spike",
|
|
64
|
+
"project": "<project>",
|
|
65
|
+
"id": "$SPIKE_ID",
|
|
66
|
+
"status": "pending",
|
|
67
|
+
"owner": { "kind": "agent", "id": "researcher" },
|
|
68
|
+
"title": "<first 80 chars of unknown>",
|
|
69
|
+
"parent_rfc": { "project": "<project>", "id": "$RFC_ID" },
|
|
70
|
+
"question": "<unknown>",
|
|
71
|
+
"method": "research"
|
|
72
|
+
}
|
|
73
|
+
EOF
|
|
74
|
+
cloverleaf-cli save-spike <repo_root> /tmp/spike-$SPIKE_ID.json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then transition RFC:
|
|
78
|
+
```
|
|
79
|
+
cloverleaf-cli advance-rfc <repo_root> <RFC-ID> spike-in-flight agent
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**If `unknowns.length === 0`:** transition RFC directly to planning:
|
|
83
|
+
```
|
|
84
|
+
cloverleaf-cli advance-rfc <repo_root> <RFC-ID> planning agent
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
10. Clean up temp files. Commit state files under `.cloverleaf/`:
|
|
88
|
+
```bash
|
|
89
|
+
git add .cloverleaf/rfcs/ .cloverleaf/spikes/ .cloverleaf/events/
|
|
90
|
+
git commit -m "cloverleaf: draft RFC $RFC_ID + spike emission"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
11. Report: RFC body populated; N spikes created (list IDs); RFC status now `spike-in-flight` or `planning`.
|
|
94
|
+
|
|
95
|
+
## Notes
|
|
96
|
+
|
|
97
|
+
- This skill does NOT prompt for human input. It's a pure agent step.
|
|
98
|
+
- The orchestrator (`/cloverleaf-discover`) invokes this plus manages bounces and gates.
|
|
99
|
+
- For a re-draft after spikes complete, invoke this skill again with the same RFC-ID — it will pick up completed spikes and may revise the RFC body.
|