@hanzlaa/rcode 4.8.0 → 4.9.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 +7 -5
- package/cli/install.js +16 -13
- package/dist/rcode.js +31 -31
- package/package.json +1 -1
- package/rcode/agents/rcode-code-reviewer.md +1 -1
- package/rcode/agents/rcode-docs-auditor.md +1 -1
- package/rcode/agents/rcode-edge-case-hunter.md +1 -1
- package/rcode/agents/rcode-security-adversary.md +1 -1
- package/rcode/agents/rcode-security-auditor.md +1 -1
- package/rcode/agents/rcode-sprint-checker.md +1 -1
- package/rcode/agents/rcode-verifier.md +1 -1
- package/rcode/bin/rcode-tools.cjs +106 -0
- package/rcode/references/git-preflight.md +5 -2
- package/rcode/references/output-format.md +5 -5
- package/rcode/workflows/add-phase.md +33 -14
- package/rcode/workflows/execute-sprint.md +3 -4
- package/rcode/workflows/execute-waves.md +25 -32
- package/rcode/workflows/execute.md +22 -19
- package/rcode/workflows/init.md +10 -2
- package/rcode/workflows/plan-research-validation.md +10 -5
- package/rcode/workflows/plan-spawn-planner.md +9 -13
- package/rcode/workflows/plan.md +2 -2
- package/rcode/workflows/scaffold-skill.md +19 -1
- package/rcode/workflows/secure-phase.md +7 -1
- package/rcode/workflows/validate-phase.md +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "rcode — the AI team that never forgets. Persistent memory, specialist agents, and slash commands for AI IDEs. Works in Claude Code, Cursor, Gemini, VS Code, and Antigravity.",
|
|
5
5
|
"main": "cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -6,7 +6,7 @@ color: yellow
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
@.rcode/references/response-style.md
|
|
9
|
-
@.rcode/references/karpathy-guidelines
|
|
9
|
+
@.rcode/references/karpathy-guidelines.md
|
|
10
10
|
@.rcode/references/no-unauthorized-git-ops.md
|
|
11
11
|
@.rcode/references/auditor-shared-checklists.md
|
|
12
12
|
@.rcode/references/docs-auditor-playbook.md
|
|
@@ -5402,6 +5402,109 @@ function cmdPlanCheckWaveOverlaps(rawArgs) {
|
|
|
5402
5402
|
return { phase: phaseArg, phase_dir: path.relative(PROJECT_ROOT, phaseDir), plans_checked: plans.length, conflicts };
|
|
5403
5403
|
}
|
|
5404
5404
|
|
|
5405
|
+
/**
|
|
5406
|
+
* Deterministic frontend/backend glob check for classify-plan (issue #1021).
|
|
5407
|
+
* Mirrors the FRONTEND_GLOBS / BACKEND_GLOBS rules formerly hand-applied by
|
|
5408
|
+
* the orchestrating LLM in execute-waves.md — kept here as the single source
|
|
5409
|
+
* of truth so both the CLI and the workflow doc describe the same behavior.
|
|
5410
|
+
*/
|
|
5411
|
+
function matchesFrontendGlob(file) {
|
|
5412
|
+
const f = String(file || '').toLowerCase();
|
|
5413
|
+
if (/\.(tsx|jsx|css)$/.test(f)) return true;
|
|
5414
|
+
return f.includes('client') || f.includes('ui');
|
|
5415
|
+
}
|
|
5416
|
+
function matchesBackendGlob(file) {
|
|
5417
|
+
const f = String(file || '').toLowerCase();
|
|
5418
|
+
return f.includes('api') || f.includes('server') || f.includes('db') || f.includes('service');
|
|
5419
|
+
}
|
|
5420
|
+
|
|
5421
|
+
const CLASSIFY_PLAN_ROUTE = { frontend: 'rcode-haitham', backend: 'rcode-yousef', 'full-stack': 'rcode-hanzla', other: 'rcode-executor' };
|
|
5422
|
+
|
|
5423
|
+
function classifyPlanFiles(files, objective) {
|
|
5424
|
+
const touchesFrontend = files.some(matchesFrontendGlob);
|
|
5425
|
+
const touchesBackend = files.some(matchesBackendGlob);
|
|
5426
|
+
let classification;
|
|
5427
|
+
if (touchesFrontend && touchesBackend) classification = 'full-stack';
|
|
5428
|
+
else if (touchesFrontend) classification = 'frontend';
|
|
5429
|
+
else if (touchesBackend) classification = 'backend';
|
|
5430
|
+
else classification = 'other';
|
|
5431
|
+
|
|
5432
|
+
if (classification === 'other') {
|
|
5433
|
+
const obj = String(objective || '').toLowerCase();
|
|
5434
|
+
const frontendKeywords = ['react', 'component', 'ui', 'css', 'tailwind', 'frontend', 'client-side', 'accessibility', 'a11y'];
|
|
5435
|
+
const backendKeywords = ['api', 'endpoint', 'database', 'schema', 'service', 'queue', 'backend', 'server-side'];
|
|
5436
|
+
if (frontendKeywords.some((k) => obj.includes(k))) classification = 'frontend';
|
|
5437
|
+
else if (backendKeywords.some((k) => obj.includes(k))) classification = 'backend';
|
|
5438
|
+
}
|
|
5439
|
+
return classification;
|
|
5440
|
+
}
|
|
5441
|
+
|
|
5442
|
+
/**
|
|
5443
|
+
* classify-plan — deterministic replacement for execute-waves.md's
|
|
5444
|
+
* hand-computed FRONTEND_GLOBS/BACKEND_GLOBS classification (issue #1021).
|
|
5445
|
+
* A live execution run showed the orchestrating LLM never actually carried
|
|
5446
|
+
* out the prose pseudocode, so a plan with a "db"-containing path still fell
|
|
5447
|
+
* back to rcode-executor instead of rcode-yousef.
|
|
5448
|
+
*
|
|
5449
|
+
* Two call shapes:
|
|
5450
|
+
* classify-plan <phase> <plan-id> — reads files_modified/objective from the plan's SPRINT.md
|
|
5451
|
+
* classify-plan --files=a,b,c --objective="..." — classify an already-parsed list directly
|
|
5452
|
+
*/
|
|
5453
|
+
function cmdClassifyPlan(args) {
|
|
5454
|
+
const flags = {};
|
|
5455
|
+
const positional = [];
|
|
5456
|
+
for (const t of args) {
|
|
5457
|
+
if (t.startsWith('--files=')) flags.files = t.slice('--files='.length);
|
|
5458
|
+
else if (t.startsWith('--objective=')) flags.objective = t.slice('--objective='.length);
|
|
5459
|
+
else positional.push(t);
|
|
5460
|
+
}
|
|
5461
|
+
|
|
5462
|
+
let files = [];
|
|
5463
|
+
let objective = '';
|
|
5464
|
+
|
|
5465
|
+
if (flags.files !== undefined || flags.objective !== undefined) {
|
|
5466
|
+
files = flags.files ? flags.files.split(',').map((s) => s.trim()).filter(Boolean) : [];
|
|
5467
|
+
objective = flags.objective || '';
|
|
5468
|
+
} else {
|
|
5469
|
+
const [phaseArg, planArg] = positional;
|
|
5470
|
+
if (!phaseArg || !planArg) {
|
|
5471
|
+
throw new Error('Usage: classify-plan <phase> <plan-id> OR classify-plan --files=a,b,c --objective="text"');
|
|
5472
|
+
}
|
|
5473
|
+
const phasesDir = path.join(PLANNING_DIR, 'phases');
|
|
5474
|
+
const norm = phaseArg.replace(/^0+/, '') || '0';
|
|
5475
|
+
let phaseDir = null;
|
|
5476
|
+
if (fs.existsSync(phasesDir)) {
|
|
5477
|
+
for (const d of fs.readdirSync(phasesDir)) {
|
|
5478
|
+
const m = d.match(/^(\d+)(?:[-.])/);
|
|
5479
|
+
if (m && (m[1].replace(/^0+/, '') || '0') === norm) { phaseDir = path.join(phasesDir, d); break; }
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
if (!phaseDir) throw new Error(`Phase not found: ${phaseArg}`);
|
|
5483
|
+
let planFile = null;
|
|
5484
|
+
for (const file of fs.readdirSync(phaseDir).filter((f) => /-SPRINT\.md$/i.test(f)).sort()) {
|
|
5485
|
+
const stem = file.replace(/-SPRINT\.md$/i, '');
|
|
5486
|
+
if (stem === planArg || stem.endsWith(`-${planArg}`)) { planFile = file; break; }
|
|
5487
|
+
const text = fs.readFileSync(path.join(phaseDir, file), 'utf8');
|
|
5488
|
+
const { frontmatter } = parseFrontmatter(text);
|
|
5489
|
+
if ((frontmatter.sprint || frontmatter.plan) === planArg) { planFile = file; break; }
|
|
5490
|
+
}
|
|
5491
|
+
if (!planFile) throw new Error(`Plan not found: ${planArg} in phase ${phaseArg}`);
|
|
5492
|
+
const text = fs.readFileSync(path.join(phaseDir, planFile), 'utf8');
|
|
5493
|
+
const { frontmatter, body } = parseFrontmatter(text);
|
|
5494
|
+
let block = '';
|
|
5495
|
+
if (text.startsWith('---\n')) {
|
|
5496
|
+
const end = text.indexOf('\n---\n', 4);
|
|
5497
|
+
if (end !== -1) block = text.slice(4, end);
|
|
5498
|
+
}
|
|
5499
|
+
files = fmListField(block, 'files_modified');
|
|
5500
|
+
const objMatch = body.match(/^##\s+(?:Objective|Goal)\s*\n+([^\n]+)/mi);
|
|
5501
|
+
objective = objMatch ? objMatch[1].trim() : (frontmatter.goal || '').replace(/^["']|["']$/g, '');
|
|
5502
|
+
}
|
|
5503
|
+
|
|
5504
|
+
const classification = classifyPlanFiles(files, objective);
|
|
5505
|
+
return { classification, subagent_type: CLASSIFY_PLAN_ROUTE[classification], files_checked: files.length };
|
|
5506
|
+
}
|
|
5507
|
+
|
|
5405
5508
|
/** phases list — directory inventory under .planning/phases with optional --type filter and --pick path. */
|
|
5406
5509
|
function cmdPhasesList(args) {
|
|
5407
5510
|
const argv = Array.isArray(args) ? args : String(args || '').trim().split(/\s+/).filter(Boolean);
|
|
@@ -6658,6 +6761,9 @@ async function main() {
|
|
|
6658
6761
|
case 'phase-plan-index':
|
|
6659
6762
|
result = cmdPhasePlanIndex(args.join(' '));
|
|
6660
6763
|
break;
|
|
6764
|
+
case 'classify-plan':
|
|
6765
|
+
result = cmdClassifyPlan(args);
|
|
6766
|
+
break;
|
|
6661
6767
|
case 'phases':
|
|
6662
6768
|
if (args[0] === 'list') { result = cmdPhasesList(args.slice(1)); if (result === undefined) return; }
|
|
6663
6769
|
else { console.error('Unknown phases subcommand. Valid: list'); process.exit(1); }
|
|
@@ -14,8 +14,11 @@ Run these read-only commands in order. Any failure halts the workflow with the f
|
|
|
14
14
|
# Check 1: working tree clean
|
|
15
15
|
DIRTY=$(git status --porcelain 2>/dev/null)
|
|
16
16
|
|
|
17
|
-
# Check 2: not on a protected branch
|
|
17
|
+
# Check 2: not on a protected branch (skipped entirely when `git.branching_strategy`
|
|
18
|
+
# config is `none` — committing directly to main/master is the deliberately configured
|
|
19
|
+
# workflow in that case)
|
|
18
20
|
BRANCH=$(git branch --show-current 2>/dev/null)
|
|
21
|
+
BRANCHING_STRATEGY=$(node .rcode/bin/rcode-tools.cjs config-get git.branching_strategy 2>/dev/null)
|
|
19
22
|
PROTECTED="main master develop v2-prototype"
|
|
20
23
|
|
|
21
24
|
# Check 3: branch follows naming convention
|
|
@@ -35,7 +38,7 @@ fi
|
|
|
35
38
|
The workflow MUST stop and print the banner below if ANY of:
|
|
36
39
|
|
|
37
40
|
- `DIRTY` is non-empty AND user did not pass `--allow-dirty`
|
|
38
|
-
- `BRANCH` is in `$PROTECTED` AND user did not pass `--on-main`
|
|
41
|
+
- `BRANCH` is in `$PROTECTED` AND `BRANCHING_STRATEGY` is not `none` AND user did not pass `--on-main`
|
|
39
42
|
- `BRANCH_OK` is `no` AND user did not pass `--allow-dirty` (branch-name lint is advisory if working tree is dirty AND user accepted the dirty override)
|
|
40
43
|
- `OUT_OF_SCOPE` is non-empty AND user did not pass `--allow-scope-drift`
|
|
41
44
|
|
|
@@ -42,7 +42,7 @@ Use for major workflow transitions.
|
|
|
42
42
|
|
|
43
43
|
```
|
|
44
44
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
45
|
-
|
|
45
|
+
rcode ► {STAGE NAME}
|
|
46
46
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -69,7 +69,7 @@ Use this when a router command dispatches to another command:
|
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
72
|
-
|
|
72
|
+
rcode ► ROUTING
|
|
73
73
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
74
74
|
|
|
75
75
|
Input: {user's question or intent}
|
|
@@ -328,7 +328,7 @@ Use standard markdown pipe tables with status symbols:
|
|
|
328
328
|
**Majlis banner** (multi-agent council):
|
|
329
329
|
```
|
|
330
330
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
331
|
-
|
|
331
|
+
rcode ► MAJLIS CONVENING
|
|
332
332
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
333
333
|
```
|
|
334
334
|
|
|
@@ -354,7 +354,7 @@ the banner, not inside it.
|
|
|
354
354
|
|
|
355
355
|
```
|
|
356
356
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
357
|
-
|
|
357
|
+
rcode ► PLANNING SPRINT 01.1
|
|
358
358
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
359
359
|
التخطيط للسباق 01.1 — يرجى الانتظار
|
|
360
360
|
```
|
|
@@ -389,7 +389,7 @@ translated prose goes outside the art, on its own line(s).
|
|
|
389
389
|
|
|
390
390
|
- Varying box/banner widths within same output
|
|
391
391
|
- Mixing banner styles (`===`, `---`, `***`)
|
|
392
|
-
- Skipping `
|
|
392
|
+
- Skipping `rcode ►` prefix in stage banners
|
|
393
393
|
- Random emoji (`🚀`, `✨`, `💫`) outside the approved set
|
|
394
394
|
- Missing Next Up block after workflow completions
|
|
395
395
|
- Hardcoding references to other methodologies in rcode's UX
|
|
@@ -29,11 +29,21 @@ Exit.
|
|
|
29
29
|
Load phase operation context:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
INIT=$(node ".rcode/bin/rcode-tools.cjs" init phase-op "
|
|
32
|
+
INIT=$(node ".rcode/bin/rcode-tools.cjs" init phase-op "1" 2>/dev/null)
|
|
33
33
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
`init phase-op` only populates the phase-aware fields (`roadmap_exists`,
|
|
37
|
+
`planning_exists`, `phase_found`, ...) when its `question` argument's first
|
|
38
|
+
token parses as an integer `> 0` (rcode-tools.cjs's `phase-op` handler gates
|
|
39
|
+
on `phaseNum > 0`). add-phase has no target phase to pass — it's creating a
|
|
40
|
+
new one — so a placeholder is required. `"0"` fails that guard, silently
|
|
41
|
+
dropping `roadmap_exists` from every response (#1017); `"1"` satisfies it.
|
|
42
|
+
The dummy value only affects unused fields like `phase_found`/`phase_name`
|
|
43
|
+
(harmless here) — `roadmap_exists` itself is purely a file-existence check
|
|
44
|
+
and doesn't depend on phase 1 actually existing.
|
|
45
|
+
|
|
46
|
+
If `INIT` is empty, print error and exit:
|
|
37
47
|
```
|
|
38
48
|
Error: rcode-tools init failed. Verify .rcode/ is installed and state.json is valid.
|
|
39
49
|
```
|
|
@@ -92,7 +102,13 @@ The CLI handles:
|
|
|
92
102
|
- Creating the phase directory (`.planning/phases/{NN}-{slug}/`)
|
|
93
103
|
- Inserting the phase entry into ROADMAP.md with Goal, Depends on, and Plans sections
|
|
94
104
|
|
|
95
|
-
Extract from result: `phase_number`, `padded`, `name`, `slug`, `directory
|
|
105
|
+
Extract from result: `phase_number`, `padded`, `name`, `slug`, `directory`,
|
|
106
|
+
`milestone_health` (object: `open_phases`, `recommendation`, `threshold_should`,
|
|
107
|
+
`threshold_consider`), `nudge` (present only when `recommendation` isn't
|
|
108
|
+
`healthy` — a ready-to-print one-liner naming the milestone). `phase add`
|
|
109
|
+
computes these via `milestoneCloseNudge()` (issue #942) as part of the same
|
|
110
|
+
call, so `milestone_health_check` below reads them straight off `$RESULT`
|
|
111
|
+
instead of re-deriving them.
|
|
96
112
|
|
|
97
113
|
**If `BULK_MODE=true`:** after the CLI returns, write the bulk body to `${directory}/TASKS.md` per the structure defined in `detect_task_list`. This step is non-destructive — it only ADDs a TASKS.md file inside the new phase directory.
|
|
98
114
|
</step>
|
|
@@ -110,26 +126,30 @@ If "Roadmap Evolution" section doesn't exist, create it.
|
|
|
110
126
|
</step>
|
|
111
127
|
|
|
112
128
|
<step name="milestone_health_check">
|
|
113
|
-
After the phase is added,
|
|
129
|
+
After the phase is added, read the milestone-health gauge (issue #718)
|
|
130
|
+
straight off the `phase add` result captured in `add_phase` — no extra
|
|
131
|
+
subprocess calls. Previously this step spawned a separate `milestone-health`
|
|
132
|
+
call plus 3 `node -e` JSON field extractions to re-derive data that `phase
|
|
133
|
+
add` already returns inline via `milestoneCloseNudge()` (#942); that was
|
|
134
|
+
4 wasted calls per phase-add for data already sitting in `$RESULT` (#1018).
|
|
114
135
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
MILESTONE_NAME=$(echo "$HEALTH" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{console.log(JSON.parse(s).milestone||'')}catch{console.log('')}})")
|
|
136
|
+
```
|
|
137
|
+
RECOMMENDATION="$RESULT.milestone_health.recommendation" # e.g. healthy | consider-closing | should-close
|
|
138
|
+
OPEN_COUNT="$RESULT.milestone_health.open_phases"
|
|
139
|
+
NUDGE="$RESULT.nudge" # ready-to-print, names the milestone; absent when healthy
|
|
120
140
|
```
|
|
121
141
|
|
|
122
142
|
If `RECOMMENDATION` is `should-close` (≥12 open phases), surface a hard nudge:
|
|
123
143
|
|
|
124
144
|
```
|
|
125
|
-
⚠
|
|
145
|
+
⚠ {NUDGE}
|
|
126
146
|
|
|
127
147
|
Phase {N} is now in this milestone, but the milestone is well past the
|
|
128
148
|
12-phase threshold for considering closure. Phases are accumulating without
|
|
129
149
|
a milestone boundary — historically this is where roadmaps lose structure.
|
|
130
150
|
|
|
131
151
|
Recommended next step:
|
|
132
|
-
/rcode-complete-milestone close
|
|
152
|
+
/rcode-complete-milestone close the milestone cleanly + archive done phases
|
|
133
153
|
/rcode-new-milestone start a fresh milestone for ongoing work
|
|
134
154
|
|
|
135
155
|
If you genuinely want a giant single-milestone roadmap, ignore this and
|
|
@@ -139,11 +159,10 @@ continue. The threshold is conservative on purpose.
|
|
|
139
159
|
If `RECOMMENDATION` is `consider-closing` (8-11 open phases), softer nudge:
|
|
140
160
|
|
|
141
161
|
```
|
|
142
|
-
ℹ
|
|
143
|
-
Consider /rcode-complete-milestone before adding more.
|
|
162
|
+
ℹ {NUDGE}
|
|
144
163
|
```
|
|
145
164
|
|
|
146
|
-
If `RECOMMENDATION` is `healthy
|
|
165
|
+
If `RECOMMENDATION` is `healthy` or `milestone_health` is absent (no state.json / no milestone), say nothing.
|
|
147
166
|
</step>
|
|
148
167
|
|
|
149
168
|
<step name="completion">
|
|
@@ -198,11 +198,10 @@ Deviations are normal — handle via rules below.
|
|
|
198
198
|
- `type="auto"`: if `tdd="true"` → TDD execution. Implement with deviation rules + auth gates. Verify done criteria. Commit (see task_commit). Track hash for Summary.
|
|
199
199
|
- `type="checkpoint:*"`: STOP → checkpoint_protocol → wait for user → continue only after confirmation.
|
|
200
200
|
- **Task completion precedence (when signals conflict):**
|
|
201
|
-
1. `<verify><automated>` — machine-executable shell commands. **Highest authority.** If these pass, the task is done. If these fail, the task is NOT done — regardless of what `<
|
|
201
|
+
1. `<verify><automated>` — machine-executable shell commands. **Highest authority.** If these pass, the task is done. If these fail, the task is NOT done — regardless of what `<done>` says.
|
|
202
202
|
2. `<done>` — single observable sentence. Use as the human-readable confirmation once automated checks pass.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
- **MANDATORY acceptance_criteria check:** After completing each task, if it has `<acceptance_criteria>`, verify EVERY criterion before moving to the next task. Use grep, file reads, or CLI commands to confirm each criterion. If any criterion fails, fix the implementation before proceeding. Do not skip criteria or mark them as "will verify later".
|
|
203
|
+
- If `<verify><automated>` is absent: fall back to `<done>` alone. `<evidence>` (grep hits, line ranges, or a creates-justification recorded by the planner per issue #649) is supporting grounding, not a completion signal to re-check here — the real plan schema (planner-playbook.md, sprint.md) has no such tag.
|
|
204
|
+
- **MANDATORY completion check:** After completing each task, confirm `<verify><automated>` passes (or, if absent, that the task's `<done>` sentence is observably true). Use grep, file reads, or CLI commands to confirm. If any check fails, fix the implementation before proceeding. Do not skip this or mark it as "will verify later".
|
|
206
205
|
3. Run `<verification>` checks
|
|
207
206
|
4. Confirm `<success_criteria>` met
|
|
208
207
|
5. Document deviations in Summary
|
|
@@ -73,45 +73,38 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
|
|
73
73
|
|
|
74
74
|
**Classify plan and select subagent_type (BEFORE spawning, once per plan):**
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
This used to be prose pseudocode the orchestrating LLM was expected to hand-apply
|
|
77
|
+
(FRONTEND_GLOBS/BACKEND_GLOBS matching + keyword fallback). A live execution run showed
|
|
78
|
+
that computation was never actually carried out — a plan whose `files_modified` clearly
|
|
79
|
+
matched the backend glob rule (a path containing `db`) still fell back to `rcode-executor`.
|
|
80
|
+
Classification is now a deterministic CLI call — do not hand-compute it.
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
touches_frontend = any(file matches FRONTEND_GLOBS for file in files_modified)
|
|
84
|
-
touches_backend = any(file matches BACKEND_GLOBS for file in files_modified)
|
|
85
|
-
|
|
86
|
-
if touches_frontend and touches_backend:
|
|
87
|
-
classification = "full-stack"
|
|
88
|
-
elif touches_frontend:
|
|
89
|
-
classification = "frontend"
|
|
90
|
-
elif touches_backend:
|
|
91
|
-
classification = "backend"
|
|
92
|
-
else:
|
|
93
|
-
classification = "other" # files_modified empty/absent, or no glob matched
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**If `classification` is `"other"`** (ambiguous, or `files_modified` empty/absent), fall back
|
|
97
|
-
to keyword-matching the plan's `<objective>` text before giving up:
|
|
98
|
-
- Frontend keywords (React, component, UI, CSS, Tailwind, frontend, client-side, accessibility, a11y) → `classification = "frontend"`
|
|
99
|
-
- Backend keywords (API, endpoint, database, schema, service, queue, backend, server-side) → `classification = "backend"`
|
|
100
|
-
- Neither matches (pure docs/config/infra plan) → `classification` stays `"other"`
|
|
82
|
+
Call `classify-plan` with the phase and this plan's id (it reads `files_modified` and the
|
|
83
|
+
`<objective>` directly from the plan's SPRINT.md, so no need to re-parse step 1's overlap
|
|
84
|
+
data yourself):
|
|
101
85
|
|
|
102
|
-
|
|
86
|
+
```bash
|
|
87
|
+
CLASSIFY_JSON=$(node ".rcode/bin/rcode-tools.cjs" classify-plan "$PHASE" "$PLAN_ID" 2>/dev/null)
|
|
88
|
+
SUBAGENT_TYPE=$(echo "$CLASSIFY_JSON" | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{console.log(JSON.parse(d).subagent_type)}catch{console.log('rcode-executor')}})")
|
|
89
|
+
SUBAGENT_TYPE=${SUBAGENT_TYPE:-rcode-executor}
|
|
90
|
+
```
|
|
103
91
|
|
|
104
|
-
|
|
105
|
-
|---|---|
|
|
106
|
-
| frontend | rcode-haitham |
|
|
107
|
-
| backend | rcode-yousef |
|
|
108
|
-
| full-stack | rcode-hanzla |
|
|
109
|
-
| other | rcode-executor |
|
|
92
|
+
Use the literal `subagent_type` value returned — do not second-guess or override it.
|
|
110
93
|
|
|
111
94
|
This decision is computed once per plan, before that plan's Task() spawn(s) below, and the
|
|
112
95
|
resulting `subagent_type` value is used in the Task() call template (worktree and sequential
|
|
113
96
|
modes both reuse this same value — see "Sequential mode" further below).
|
|
114
97
|
|
|
98
|
+
**Resolve executor model (once per wave, before spawning):**
|
|
99
|
+
|
|
100
|
+
`executor_model` from `init` is the raw `model_profile` string (e.g. `balanced`), not a
|
|
101
|
+
resolved model id — it must be passed through `resolve-model` first, the same way
|
|
102
|
+
`code_review_gate` in execute.md resolves `REVIEWER_MODEL` before its Task() spawn.
|
|
103
|
+
```bash
|
|
104
|
+
EXECUTOR_MODEL=$(node ".rcode/bin/rcode-tools.cjs" resolve-model executor 2>/dev/null | node -e "let d='';process.stdin.on('data',c=>d+=c).on('end',()=>{try{console.log(JSON.parse(d).model)}catch{console.log('')}})" || echo "sonnet")
|
|
105
|
+
EXECUTOR_MODEL=${EXECUTOR_MODEL:-sonnet}
|
|
106
|
+
```
|
|
107
|
+
|
|
115
108
|
**Worktree mode** (`USE_WORKTREES` is not `false`):
|
|
116
109
|
|
|
117
110
|
Before spawning, capture the current HEAD:
|
|
@@ -139,7 +132,7 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT
|
|
|
139
132
|
Task(
|
|
140
133
|
subagent_type="{subagent_type}",
|
|
141
134
|
description="Execute plan {plan_number} of phase {phase_number}",
|
|
142
|
-
model="{
|
|
135
|
+
model="${EXECUTOR_MODEL}",
|
|
143
136
|
isolation="worktree",
|
|
144
137
|
prompt="
|
|
145
138
|
<objective>
|
|
@@ -35,11 +35,12 @@ route back to the user.
|
|
|
35
35
|
4. **Branch check**: confirm current git branch is appropriate
|
|
36
36
|
for the work. Two checks, both blocking:
|
|
37
37
|
|
|
38
|
-
a. **Not on main/master without consent
|
|
39
|
-
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
a. **Not on main/master without consent** (skip entirely when `git.branching_strategy`
|
|
39
|
+
config is `none` — check via `node .rcode/bin/rcode-tools.cjs config-get
|
|
40
|
+
git.branching_strategy`): if `git branch --show-current` returns `main` or
|
|
41
|
+
`master`, refuse to execute. Suggest: `git switch -c <phase>-<plan>-<slug>`
|
|
42
|
+
(e.g. `git switch -c 8-1-aria`). User can override only by passing `--on-main`
|
|
43
|
+
to /rcode-execute and explicitly typing the override on this turn.
|
|
43
44
|
|
|
44
45
|
b. **Working tree clean enough**: if `git status --porcelain` shows
|
|
45
46
|
modified files unrelated to this phase's `files_modified` frontmatter,
|
|
@@ -49,8 +50,7 @@ route back to the user.
|
|
|
49
50
|
|
|
50
51
|
The branch name should align with the phase/plan IDs from state — check
|
|
51
52
|
`workflow.branch_pattern` config (default `<phase>-<plan>-<slug>`).
|
|
52
|
-
5. **Worktree config**: read `workflow.use_worktrees` — if true + parallelization
|
|
53
|
-
is true + no file overlaps, plans in a wave run parallel via worktrees
|
|
53
|
+
5. **Worktree config**: read `workflow.use_worktrees` — if true + no file overlaps, plans in a wave run parallel via worktrees. (`parallelization` is not a real field in `init execute`'s output — see the "initialize" step below; don't gate on it.)
|
|
54
54
|
</pre_flight>
|
|
55
55
|
|
|
56
56
|
<insight_block>
|
|
@@ -238,14 +238,16 @@ If `INIT` is empty or `INIT.ok` is false, print error and exit:
|
|
|
238
238
|
Error: rcode-tools init failed. Verify .rcode/ is installed and state.json is valid.
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
Parse JSON for: `executor_model`, `verifier_model`, `
|
|
241
|
+
Parse JSON for these real, top-level fields: `executor_model`, `verifier_model`, `phase_dir`, `plans`, `state_exists`, `response_language`. Fields commonly assumed to exist but that are NOT top-level (verified live this session against `init execute`'s real output, `cmdInitExecute` in rcode-tools.cjs): `branching_strategy` is nested under `config.branching_strategy`; `commit_docs` doesn't exist (closest real value is `config.commit_planning`, a `"true"`/`"false"` string); `parallelization` has no source anywhere (not top-level, not under `config`, not in `phase-plan-index`'s output either); `branch_name` isn't returned (the `handle_branching` step below now computes it from config instead); `phase_name` isn't derivable either (`phase_dir`'s basename is a slug, not the human-readable name — read ROADMAP.md if a step needs it); `incomplete_plans`/`incomplete_count` don't exist (`plans[]` items only carry `{path, depends_on, wave, plan}`, no completion field); `roadmap_exists`/`phase_req_ids` are returned only by the separate `init sprint-plan` command, not `init execute`.
|
|
242
|
+
Derivable, not literal: `phase_found` as `phase_dir !== null`; `phase_number` as the `target` field (the raw phase argument as passed, e.g. `"45"`); `phase_slug` from `phase_dir`'s basename (the part after the first `-`); `plan_count` as `plans.length`. Downstream `${PHASE_NUMBER}`/`${PLAN_COUNT}` references later in this workflow (snapshot tag, review prompts, `phase complete`, etc.) resolve from `target`/`plans.length` per these derivations; `${PHASE_NAME}` and `${INCOMPLETE_COUNT}` have no source here — read `PHASE_NAME` from ROADMAP.md if a later step needs it, and treat `INCOMPLETE_COUNT` as unknown until `phase-plan-index` runs in `discover_and_group_plans` (which does return a real per-plan `has_summary` completion signal).
|
|
242
243
|
|
|
243
244
|
**If `response_language` is set:** Include `response_language: {value}` in all spawned subagent prompts so any user-facing output stays in the configured language.
|
|
244
245
|
|
|
245
246
|
Read worktree config:
|
|
246
247
|
|
|
247
248
|
```bash
|
|
248
|
-
USE_WORKTREES=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.use_worktrees 2>/dev/null
|
|
249
|
+
USE_WORKTREES=$(node ".rcode/bin/rcode-tools.cjs" config-get workflow.use_worktrees 2>/dev/null)
|
|
250
|
+
USE_WORKTREES=${USE_WORKTREES:-true} # config-get exits 0 with empty output when key absent; || fallback won't fire
|
|
249
251
|
```
|
|
250
252
|
|
|
251
253
|
When `USE_WORKTREES` is `false`, all executor agents run without `isolation="worktree"` — they execute sequentially on the main working tree instead of in parallel worktrees.
|
|
@@ -265,16 +267,17 @@ When `CONTEXT_WINDOW >= 500000` (1M-class models), subagent prompts include rich
|
|
|
265
267
|
- Verifier agents receive all SPRINT.md, SUMMARY.md, CONTEXT.md files plus REQUIREMENTS.md
|
|
266
268
|
- This enables cross-phase awareness and history-aware verification
|
|
267
269
|
|
|
268
|
-
**If `
|
|
269
|
-
**If `
|
|
270
|
+
**If `phase_dir` is `null` (derived `phase_found` false):** Error — phase directory not found. Run `/rcode-status` to inspect state or `/rcode-plan {N}` to create the phase.
|
|
271
|
+
**If `plans.length` is 0 (derived `plan_count` 0):** Error — no plans found in phase. Run `/rcode-plan {N}` to generate plans or `/rcode-help` for the command surface.
|
|
270
272
|
**If `state_exists` is false but `.planning/` exists:** Offer reconstruct or continue.
|
|
271
273
|
|
|
272
|
-
|
|
274
|
+
`parallelization` is not a real field in `init execute`'s output (see the "initialize" step's field notes above) — this line currently documents behavior with no data source; don't treat it as a working toggle until a real source is wired in.
|
|
273
275
|
|
|
274
276
|
**Runtime detection for Copilot:**
|
|
275
277
|
Check if the current runtime is Copilot by testing for the `@rcode-executor` agent pattern
|
|
276
278
|
or absence of the `Task()` subagent API. If running under Copilot, force sequential inline
|
|
277
|
-
execution
|
|
279
|
+
execution unconditionally (there is no real `parallelization` toggle to override — see the
|
|
280
|
+
"initialize" step's field notes above) — Copilot's subagent completion
|
|
278
281
|
signals are unreliable (see `<runtime_compatibility>`). Set `COPILOT_SEQUENTIAL=true`
|
|
279
282
|
internally and skip the `execute_waves` step in favor of `check_interactive_mode`'s
|
|
280
283
|
inline path for each plan.
|
|
@@ -352,7 +355,7 @@ Check `branching_strategy` from init:
|
|
|
352
355
|
|
|
353
356
|
**"none":** Skip, continue on current branch.
|
|
354
357
|
|
|
355
|
-
**"phase" or "milestone":**
|
|
358
|
+
**"phase" or "milestone":** `init execute` does not return `branch_name` (see the "initialize" step's field notes above) — compute `BRANCH_NAME` from `workflow.branch_pattern` config (default `<phase>-<plan>-<slug>`) before running:
|
|
356
359
|
```bash
|
|
357
360
|
git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
|
|
358
361
|
```
|
|
@@ -361,7 +364,7 @@ All subsequent commits go to this branch. User handles merging.
|
|
|
361
364
|
</step>
|
|
362
365
|
|
|
363
366
|
<step name="validate_phase">
|
|
364
|
-
From init JSON: `phase_dir
|
|
367
|
+
From init JSON: `phase_dir` (real); `plan_count` derives as `plans.length`; `incomplete_count` has no source at this point (see the "initialize" step's field notes) — treat as unknown until `phase-plan-index` runs.
|
|
365
368
|
|
|
366
369
|
Report: "Found {plan_count} plans in {phase_dir} ({incomplete_count} incomplete)"
|
|
367
370
|
|
|
@@ -527,7 +530,7 @@ Selected wave finished successfully. This phase still has incomplete plans, so p
|
|
|
527
530
|
<step name="run_verify_commands">
|
|
528
531
|
**Run per-task `<verify>` shell commands from all completed SPRINT.md plans.**
|
|
529
532
|
|
|
530
|
-
After all executor agents finish, extract and run any `<verify>` blocks defined in plan tasks. These are the machine-executable
|
|
533
|
+
After all executor agents finish, extract and run any `<verify>` blocks defined in plan tasks. These are the machine-executable proof that a task's `<done>` criteria are met — the plan schema has no such tag; `<verify><automated>` plus `<evidence>` grounding are what the planner and executor actually emit and enforce.
|
|
531
534
|
|
|
532
535
|
```bash
|
|
533
536
|
# Extract all <verify> blocks from all SPRINT.md files for this phase
|
|
@@ -745,10 +748,10 @@ fi
|
|
|
745
748
|
```
|
|
746
749
|
⚠ Phase {X} EXECUTED but not yet verified.
|
|
747
750
|
|
|
748
|
-
The following
|
|
751
|
+
The following task completion criteria require human verification before
|
|
749
752
|
the phase can advance to `status: complete`:
|
|
750
753
|
|
|
751
|
-
{list
|
|
754
|
+
{list each task's <done> sentence from SPRINT.md}
|
|
752
755
|
|
|
753
756
|
Recommended next steps:
|
|
754
757
|
/rcode-add-tests {X} — generate unit + E2E tests before UAT
|
|
@@ -761,7 +764,7 @@ fi
|
|
|
761
764
|
**If `VERIFICATION_STATUS` is `fail`:**
|
|
762
765
|
|
|
763
766
|
1. Mark the phase as `status: executed` (so /rcode-plan --gaps can run a closure cycle).
|
|
764
|
-
2. Surface the failed
|
|
767
|
+
2. Surface the tasks whose `<done>` criteria failed human verification.
|
|
765
768
|
3. STOP. Don't mark complete on a failing verification.
|
|
766
769
|
|
|
767
770
|
**Only when `VERIFICATION_STATUS` is `pass`** — proceed to `update_roadmap` below.
|
package/rcode/workflows/init.md
CHANGED
|
@@ -279,10 +279,10 @@ if [ -s .rcode/context/active.md ] && ! grep -q "Run \`/rcode" .rcode/context/ac
|
|
|
279
279
|
fi
|
|
280
280
|
```
|
|
281
281
|
|
|
282
|
-
After writing both files, refresh the memory bank fingerprint so staleness checks see the project as fresh:
|
|
282
|
+
After writing both files, refresh the memory bank fingerprint so staleness checks see the project as fresh. `context refresh` reads `.rcode/sources.yaml`, but nothing in rcode ever creates that file (the installer only scaffolds the unrelated `.rcode/brain/sources.yaml`, used by `brain pull`) — so on every fresh project this call is a guaranteed no-op. Skip it when the file is known not to exist rather than spending a call to learn that (#1018):
|
|
283
283
|
|
|
284
284
|
```bash
|
|
285
|
-
node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
|
|
285
|
+
test -f .rcode/sources.yaml && node .rcode/bin/rcode-tools.cjs context refresh >/dev/null 2>&1 || true
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
## Step 4c — Scaffold CLAUDE.md / AGENTS.md if missing
|
|
@@ -327,6 +327,14 @@ Or strategic question about the codebase:
|
|
|
327
327
|
/rcode-council {your question}
|
|
328
328
|
```
|
|
329
329
|
|
|
330
|
+
**In all three cases, append this tip:**
|
|
331
|
+
```
|
|
332
|
+
Tip: run /rcode-enable-hooks to turn on a one-line project status primer at
|
|
333
|
+
the start of every session, plus 9 other opt-in guardrails (read-before-edit
|
|
334
|
+
checks, dangerous-command blocking, auto-formatting). Off by default so a
|
|
335
|
+
fresh install never surprises you.
|
|
336
|
+
```
|
|
337
|
+
|
|
330
338
|
**If `returning` with `--reset`:**
|
|
331
339
|
```
|
|
332
340
|
✓ rcode reconfigured. Prior state preserved in state.json.
|