@cloverleaf/reference-impl 0.6.2 → 0.6.4
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 +1 -1
- package/dist/cli.mjs +24 -1
- package/dist/prep-worktree.mjs +1 -0
- package/dist/walker-config.mjs +63 -0
- package/lib/cli.ts +24 -1
- package/lib/prep-worktree.ts +1 -0
- package/lib/walker-config.ts +90 -0
- package/package.json +2 -2
- package/skills/cloverleaf-run/SKILL.md +3 -1
- package/skills/cloverleaf-run-plan/SKILL.md +38 -1
package/README.md
CHANGED
|
@@ -152,7 +152,7 @@ The Reviewer never switches branches. It reads files via `git show` and runs tes
|
|
|
152
152
|
|
|
153
153
|
## Package layout
|
|
154
154
|
|
|
155
|
-
- `lib/` — TypeScript library used by the CLI. State, events, feedback, IDs, paths. Includes `buildBaselinePath(repoRoot, browser, slug, viewport)` (`lib/visual-diff.ts`) for constructing canonical baseline paths under `.cloverleaf/baselines/{browser}/`. `lib/ui-browser.ts` exports `buildBrowserEscalationFinding` and `applyMaxCombinationsCap` (used by the UI Reviewer prompt for per-engine escalation and combination-count capping). `lib/ui-review-state.ts` exports `readUiReviewState`, `writeUiReviewState`, and `uiReviewStatePath` — the baseline-approval sidecar API for `.cloverleaf/runs/{taskId}/ui-review/state.json`. The CLI exposes `write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>` as the safe write path for baselines; it enforces the `baselines_pending` guard and uses `buildBaselinePath` internally.
|
|
155
|
+
- `lib/` — TypeScript library used by the CLI. State, events, feedback, IDs, paths. Includes `buildBaselinePath(repoRoot, browser, slug, viewport)` (`lib/visual-diff.ts`) for constructing canonical baseline paths under `.cloverleaf/baselines/{browser}/`. `lib/ui-browser.ts` exports `buildBrowserEscalationFinding` and `applyMaxCombinationsCap` (used by the UI Reviewer prompt for per-engine escalation and combination-count capping). `lib/ui-review-state.ts` exports `readUiReviewState`, `writeUiReviewState`, and `uiReviewStatePath` — the baseline-approval sidecar API for `.cloverleaf/runs/{taskId}/ui-review/state.json`. The CLI exposes `write-baseline <repoRoot> <taskId> <browser> <slug> <viewport> <sourceFile>` as the safe write path for baselines; it enforces the `baselines_pending` guard and uses `buildBaselinePath` internally. `lib/walker-config.ts` exports `loadWalkerConfig()` — reads `~/.config/cloverleaf/walker.json` (XDG-aware) for the user-level `max_concurrent` override; exposed via `cloverleaf-cli walker-default-concurrency [--explain]`.
|
|
156
156
|
- `skills/` — Claude Code skill markdown files.
|
|
157
157
|
- `prompts/` — Implementer/Reviewer subagent system prompts.
|
|
158
158
|
- `examples/toy-repo/` — standalone demo repo.
|
package/dist/cli.mjs
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* dag-detect-cycle <repoRoot> <planId>
|
|
35
35
|
* walk-state-read <repoRoot> <planId>
|
|
36
36
|
* walk-state-write <repoRoot> <walkStateJsonPath>
|
|
37
|
+
* walker-default-concurrency [--explain]
|
|
37
38
|
*/
|
|
38
39
|
import { readFileSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
39
40
|
import { dirname } from 'node:path';
|
|
@@ -58,6 +59,7 @@ import { readUiReviewState, writeUiReviewState } from './ui-review-state.mjs';
|
|
|
58
59
|
import { buildBaselinePath } from './visual-diff.mjs';
|
|
59
60
|
import { computeReadyTasks, detectCycle } from './dag-walker.mjs';
|
|
60
61
|
import { readWalkState, writeWalkState, walkStatePath } from './walk-state.mjs';
|
|
62
|
+
import { loadWalkerConfig } from './walker-config.mjs';
|
|
61
63
|
function die(msg, code = 1) {
|
|
62
64
|
process.stderr.write(msg + '\n');
|
|
63
65
|
process.exit(code);
|
|
@@ -95,7 +97,8 @@ function usage(msg) {
|
|
|
95
97
|
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
96
98
|
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
97
99
|
' walk-state-read <repoRoot> <planId>\n' +
|
|
98
|
-
' walk-state-write <repoRoot> <walkStateJsonPath>\n'
|
|
100
|
+
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
101
|
+
' walker-default-concurrency [--explain]\n');
|
|
99
102
|
process.exit(2);
|
|
100
103
|
}
|
|
101
104
|
const [, , command, ...rest] = process.argv;
|
|
@@ -485,6 +488,26 @@ try {
|
|
|
485
488
|
writeWalkState(repoRoot, state);
|
|
486
489
|
break;
|
|
487
490
|
}
|
|
491
|
+
case 'walker-default-concurrency': {
|
|
492
|
+
const explain = rest.includes('--explain');
|
|
493
|
+
let cfg;
|
|
494
|
+
try {
|
|
495
|
+
cfg = loadWalkerConfig();
|
|
496
|
+
}
|
|
497
|
+
catch (err) {
|
|
498
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
499
|
+
process.stderr.write(msg + '\n');
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
if (explain) {
|
|
503
|
+
const location = cfg.source === 'user' ? `from ${cfg.path}` : 'default';
|
|
504
|
+
process.stdout.write(`max_concurrent=${cfg.maxConcurrent} (${location})\n`);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
process.stdout.write(`${cfg.maxConcurrent}\n`);
|
|
508
|
+
}
|
|
509
|
+
process.exit(0);
|
|
510
|
+
}
|
|
488
511
|
default:
|
|
489
512
|
usage(`Unknown command: ${command}`);
|
|
490
513
|
}
|
package/dist/prep-worktree.mjs
CHANGED
|
@@ -115,6 +115,7 @@ export function prepWorktree(mainRoot, worktreePath) {
|
|
|
115
115
|
// EEXIST on vite/node_modules/.bin on second invocation).
|
|
116
116
|
primeCopy(mainStandardNm, wtStandardNm);
|
|
117
117
|
primeCopy(mainRefImplNm, wtRefImplNm);
|
|
118
|
+
primeCopy(join(resolvedMain, 'reference-impl', 'dist'), join(worktreePath, 'reference-impl', 'dist'));
|
|
118
119
|
execSync('npm run build', {
|
|
119
120
|
cwd: join(worktreePath, 'standard'),
|
|
120
121
|
stdio: 'pipe',
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the absolute path to the walker config file.
|
|
6
|
+
* Respects XDG_CONFIG_HOME if set; otherwise falls back to $HOME/.config.
|
|
7
|
+
*/
|
|
8
|
+
export function walkerConfigPath() {
|
|
9
|
+
const xdgConfigHome = process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config');
|
|
10
|
+
return join(xdgConfigHome, 'cloverleaf', 'walker.json');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Load and validate the walker configuration.
|
|
14
|
+
*
|
|
15
|
+
* Resolution rules:
|
|
16
|
+
* - File absent (ENOENT) → { maxConcurrent: 3, source: 'default' }
|
|
17
|
+
* - File present, no max_concurrent field → { maxConcurrent: 3, source: 'default' }
|
|
18
|
+
* - File present, valid positive integer → { maxConcurrent: N, source: 'user' }
|
|
19
|
+
* - Malformed JSON → throws with file path and parser detail
|
|
20
|
+
* - Invalid max_concurrent (0, negative, float, string, null) → throws with field path and actual value
|
|
21
|
+
*
|
|
22
|
+
* No AJV dependency — hand-rolled validation.
|
|
23
|
+
*/
|
|
24
|
+
export function loadWalkerConfig() {
|
|
25
|
+
const configPath = walkerConfigPath();
|
|
26
|
+
let raw;
|
|
27
|
+
try {
|
|
28
|
+
raw = readFileSync(configPath, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
const e = err;
|
|
32
|
+
if (e.code === 'ENOENT') {
|
|
33
|
+
return { maxConcurrent: 3, source: 'default', path: configPath };
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
43
|
+
throw new Error(`walker config at ${configPath} contains malformed JSON: ${detail}`);
|
|
44
|
+
}
|
|
45
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
46
|
+
throw new Error(`walker config at ${configPath}: expected a JSON object, got ${JSON.stringify(parsed)}`);
|
|
47
|
+
}
|
|
48
|
+
const doc = parsed;
|
|
49
|
+
if (!('max_concurrent' in doc)) {
|
|
50
|
+
return { maxConcurrent: 3, source: 'default', path: configPath };
|
|
51
|
+
}
|
|
52
|
+
const value = doc['max_concurrent'];
|
|
53
|
+
if (typeof value !== 'number') {
|
|
54
|
+
throw new Error(`walker config at ${configPath}: max_concurrent must be a positive integer, got ${JSON.stringify(value)}`);
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isInteger(value)) {
|
|
57
|
+
throw new Error(`walker config at ${configPath}: max_concurrent must be a positive integer (no floats), got ${value}`);
|
|
58
|
+
}
|
|
59
|
+
if (value <= 0) {
|
|
60
|
+
throw new Error(`walker config at ${configPath}: max_concurrent must be a positive integer (> 0), got ${value}`);
|
|
61
|
+
}
|
|
62
|
+
return { maxConcurrent: value, source: 'user', path: configPath };
|
|
63
|
+
}
|
package/lib/cli.ts
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* dag-detect-cycle <repoRoot> <planId>
|
|
35
35
|
* walk-state-read <repoRoot> <planId>
|
|
36
36
|
* walk-state-write <repoRoot> <walkStateJsonPath>
|
|
37
|
+
* walker-default-concurrency [--explain]
|
|
37
38
|
*/
|
|
38
39
|
|
|
39
40
|
import { readFileSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
@@ -60,6 +61,7 @@ import { readUiReviewState, writeUiReviewState } from './ui-review-state.js';
|
|
|
60
61
|
import { buildBaselinePath } from './visual-diff.js';
|
|
61
62
|
import { computeReadyTasks, detectCycle } from './dag-walker.js';
|
|
62
63
|
import { readWalkState, writeWalkState, walkStatePath } from './walk-state.js';
|
|
64
|
+
import { loadWalkerConfig } from './walker-config.js';
|
|
63
65
|
|
|
64
66
|
function die(msg: string, code = 1): never {
|
|
65
67
|
process.stderr.write(msg + '\n');
|
|
@@ -99,7 +101,8 @@ function usage(msg?: string): never {
|
|
|
99
101
|
' dag-ready-tasks <repoRoot> <planId> <maxConcurrent>\n' +
|
|
100
102
|
' dag-detect-cycle <repoRoot> <planId>\n' +
|
|
101
103
|
' walk-state-read <repoRoot> <planId>\n' +
|
|
102
|
-
' walk-state-write <repoRoot> <walkStateJsonPath>\n'
|
|
104
|
+
' walk-state-write <repoRoot> <walkStateJsonPath>\n' +
|
|
105
|
+
' walker-default-concurrency [--explain]\n'
|
|
103
106
|
);
|
|
104
107
|
process.exit(2);
|
|
105
108
|
}
|
|
@@ -499,6 +502,26 @@ try {
|
|
|
499
502
|
break;
|
|
500
503
|
}
|
|
501
504
|
|
|
505
|
+
case 'walker-default-concurrency': {
|
|
506
|
+
const explain = rest.includes('--explain');
|
|
507
|
+
let cfg: { maxConcurrent: number; source: 'user' | 'default'; path: string };
|
|
508
|
+
try {
|
|
509
|
+
cfg = loadWalkerConfig();
|
|
510
|
+
} catch (err: unknown) {
|
|
511
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
512
|
+
process.stderr.write(msg + '\n');
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
if (explain) {
|
|
516
|
+
const location =
|
|
517
|
+
cfg.source === 'user' ? `from ${cfg.path}` : 'default';
|
|
518
|
+
process.stdout.write(`max_concurrent=${cfg.maxConcurrent} (${location})\n`);
|
|
519
|
+
} else {
|
|
520
|
+
process.stdout.write(`${cfg.maxConcurrent}\n`);
|
|
521
|
+
}
|
|
522
|
+
process.exit(0);
|
|
523
|
+
}
|
|
524
|
+
|
|
502
525
|
default:
|
|
503
526
|
usage(`Unknown command: ${command}`);
|
|
504
527
|
}
|
package/lib/prep-worktree.ts
CHANGED
|
@@ -128,6 +128,7 @@ export function prepWorktree(mainRoot: string, worktreePath: string): void {
|
|
|
128
128
|
// EEXIST on vite/node_modules/.bin on second invocation).
|
|
129
129
|
primeCopy(mainStandardNm, wtStandardNm);
|
|
130
130
|
primeCopy(mainRefImplNm, wtRefImplNm);
|
|
131
|
+
primeCopy(join(resolvedMain, 'reference-impl', 'dist'), join(worktreePath, 'reference-impl', 'dist'));
|
|
131
132
|
|
|
132
133
|
execSync('npm run build', {
|
|
133
134
|
cwd: join(worktreePath, 'standard'),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface WalkerConfig {
|
|
6
|
+
maxConcurrent: number;
|
|
7
|
+
source: 'user' | 'default';
|
|
8
|
+
/** Absolute resolved path to the walker.json file (whether it exists or not). */
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the absolute path to the walker config file.
|
|
14
|
+
* Respects XDG_CONFIG_HOME if set; otherwise falls back to $HOME/.config.
|
|
15
|
+
*/
|
|
16
|
+
export function walkerConfigPath(): string {
|
|
17
|
+
const xdgConfigHome = process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config');
|
|
18
|
+
return join(xdgConfigHome, 'cloverleaf', 'walker.json');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load and validate the walker configuration.
|
|
23
|
+
*
|
|
24
|
+
* Resolution rules:
|
|
25
|
+
* - File absent (ENOENT) → { maxConcurrent: 3, source: 'default' }
|
|
26
|
+
* - File present, no max_concurrent field → { maxConcurrent: 3, source: 'default' }
|
|
27
|
+
* - File present, valid positive integer → { maxConcurrent: N, source: 'user' }
|
|
28
|
+
* - Malformed JSON → throws with file path and parser detail
|
|
29
|
+
* - Invalid max_concurrent (0, negative, float, string, null) → throws with field path and actual value
|
|
30
|
+
*
|
|
31
|
+
* No AJV dependency — hand-rolled validation.
|
|
32
|
+
*/
|
|
33
|
+
export function loadWalkerConfig(): WalkerConfig {
|
|
34
|
+
const configPath = walkerConfigPath();
|
|
35
|
+
|
|
36
|
+
let raw: string;
|
|
37
|
+
try {
|
|
38
|
+
raw = readFileSync(configPath, 'utf-8');
|
|
39
|
+
} catch (err: unknown) {
|
|
40
|
+
const e = err as NodeJS.ErrnoException;
|
|
41
|
+
if (e.code === 'ENOENT') {
|
|
42
|
+
return { maxConcurrent: 3, source: 'default', path: configPath };
|
|
43
|
+
}
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let parsed: unknown;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(raw);
|
|
50
|
+
} catch (err: unknown) {
|
|
51
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
52
|
+
throw new Error(
|
|
53
|
+
`walker config at ${configPath} contains malformed JSON: ${detail}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`walker config at ${configPath}: expected a JSON object, got ${JSON.stringify(parsed)}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const doc = parsed as Record<string, unknown>;
|
|
64
|
+
|
|
65
|
+
if (!('max_concurrent' in doc)) {
|
|
66
|
+
return { maxConcurrent: 3, source: 'default', path: configPath };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const value = doc['max_concurrent'];
|
|
70
|
+
|
|
71
|
+
if (typeof value !== 'number') {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`walker config at ${configPath}: max_concurrent must be a positive integer, got ${JSON.stringify(value)}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!Number.isInteger(value)) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`walker config at ${configPath}: max_concurrent must be a positive integer (no floats), got ${value}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (value <= 0) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`walker config at ${configPath}: max_concurrent must be a positive integer (> 0), got ${value}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { maxConcurrent: value, source: 'user', path: configPath };
|
|
90
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloverleaf/reference-impl",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
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",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
46
|
"build": "tsc -p tsconfig.build.json && node scripts/rename-to-mjs.mjs",
|
|
47
47
|
"acceptance:walker": "bash scripts/acceptance-walker.sh",
|
|
48
|
-
"prepublishOnly": "npm test && npm run build"
|
|
48
|
+
"prepublishOnly": "node scripts/check-standard-prepped.mjs && npm test && npm run build"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@cloverleaf/standard": "^0.4.0",
|
|
@@ -7,7 +7,9 @@ description: End-to-end orchestrator. Reads task.risk_class to dispatch fast lan
|
|
|
7
7
|
|
|
8
8
|
## Branch discipline
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`<repo_root>` is `$(git rev-parse --show-toplevel)` of your current working directory. In walker context (Session B inside `$WORKTREE_ROOT`), this is the worktree path, NOT the primary repo. Pass `<repo_root>` explicitly to every `cloverleaf-cli` invocation, and run `git add .cloverleaf/ && git commit` from `<repo_root>` so state-advance commits land on the worktree's current branch (`cloverleaf/<TASK-ID>` in walker mode).
|
|
11
|
+
|
|
12
|
+
Do NOT `git checkout main` from a walker worktree — main is held by the primary repo. To compare against main, use `git diff main..HEAD` or `git show main:<path>`. Sub-skills run from the worktree's current branch and stay on it; the walker (in the primary repo) does the final merge to main itself after all tasks reach final-gate.
|
|
11
13
|
|
|
12
14
|
## Per-agent bounce budget
|
|
13
15
|
|
|
@@ -20,9 +20,27 @@ description: Autonomous DAG walker for Cloverleaf Plans. Given a PLAN-ID in stat
|
|
|
20
20
|
|
|
21
21
|
1. Capture the `<PLAN-ID>` argument and optional flags:
|
|
22
22
|
|
|
23
|
-
- `--max-concurrent=N` — cap simultaneous sessions. Default `3
|
|
23
|
+
- `--max-concurrent=N` — cap simultaneous sessions. Default `3` (resolved via `cloverleaf-cli walker-default-concurrency`). Setting `--max-concurrent=1` yields serial behaviour.
|
|
24
24
|
- `--reset` — wipe `.cloverleaf/runs/plan/<PLAN-ID>/walk-state.json` and start fresh.
|
|
25
25
|
|
|
26
|
+
Resolve `MAX` and print exactly one startup info line:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
if [ -n "$MAX_FLAG" ]; then
|
|
30
|
+
MAX="$MAX_FLAG"
|
|
31
|
+
echo "max_concurrent=$MAX (from --max-concurrent flag)"
|
|
32
|
+
else
|
|
33
|
+
if ! MAX=$(cloverleaf-cli walker-default-concurrency); then
|
|
34
|
+
echo "ERROR: cloverleaf-cli walker-default-concurrency failed."
|
|
35
|
+
echo "Fix or remove \`~/.config/cloverleaf/walker.json\` and retry."
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
cloverleaf-cli walker-default-concurrency --explain
|
|
39
|
+
fi
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If `cloverleaf-cli walker-default-concurrency` exits non-zero (e.g. malformed `${XDG_CONFIG_HOME:-$HOME/.config}/cloverleaf/walker.json`), the walker stops and reports the error: **Fix or remove `~/.config/cloverleaf/walker.json` and retry.** This mirrors the step-0 uncommitted-changes stop-and-report pattern.
|
|
43
|
+
|
|
26
44
|
2. **Guard against cycles.**
|
|
27
45
|
|
|
28
46
|
```bash
|
|
@@ -178,6 +196,18 @@ description: Autonomous DAG walker for Cloverleaf Plans. Given a PLAN-ID in stat
|
|
|
178
196
|
|
|
179
197
|
If every task in the plan's `task_dag.nodes` has `state: "merged"`, print: "✓ Plan `<PLAN-ID>` complete."
|
|
180
198
|
|
|
199
|
+
## Next steps (release publishing)
|
|
200
|
+
|
|
201
|
+
Once all tasks are merged, run the following commands in order to tag and publish the release:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
git tag -a reference-impl-v<VERSION> -m "reference-impl v<VERSION>"
|
|
205
|
+
git push origin main
|
|
206
|
+
git push origin reference-impl-v<VERSION>
|
|
207
|
+
(cd reference-impl && npm publish --access public)
|
|
208
|
+
gh release create reference-impl-v<VERSION> --title "reference-impl v<VERSION>" --notes-from-tag
|
|
209
|
+
```
|
|
210
|
+
|
|
181
211
|
## Session brief template
|
|
182
212
|
|
|
183
213
|
The walker constructs a per-task `scenario_brief` roughly like:
|
|
@@ -191,6 +221,13 @@ cloverleaf/<TASK-ID> (already created from main). Task risk_class: <class>.
|
|
|
191
221
|
working directory is the worktree root, not whatever directory the session
|
|
192
222
|
inherited.**
|
|
193
223
|
|
|
224
|
+
**DO NOT run `git checkout main` from this worktree.** The `main` branch is
|
|
225
|
+
held by the primary repo — attempting to check it out here will fail because
|
|
226
|
+
the same branch cannot be checked out in two worktrees simultaneously. To
|
|
227
|
+
compare your work against main, use `git diff main..HEAD` (safe diff) or
|
|
228
|
+
`git show main:<path>` (safe file inspection). All state-advance commits must
|
|
229
|
+
stay on the worktree's current branch (`cloverleaf/<TASK-ID>`).
|
|
230
|
+
|
|
194
231
|
Plan: invoke `/cloverleaf-run <TASK-ID>`.
|
|
195
232
|
|
|
196
233
|
**DO NOT invoke `/cloverleaf-merge`**. Fast lane stops after `/cloverleaf-review`
|