@hanzlaa/rcode 4.14.0 → 4.15.1
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/cli/install.js +93 -6
- package/cli/uninstall.js +13 -0
- package/dist/rcode.js +195 -195
- package/package.json +1 -1
- package/rcode/agents/rcode-orchestrator.md +3 -0
- package/rcode/agents/rules/orchestrator/contract.md +18 -0
- package/rcode/agents/rules/roadmapper/detailed-guide.md +4 -2
- package/rcode/agents/rules/sprint-checker/dimensions.md +45 -0
- package/rcode/agents/rules/sprint-checker/plan-quality-rubric.md +110 -0
- package/rcode/bin/lib/customize.cjs +115 -0
- package/rcode/bin/lib/memlog.cjs +124 -0
- package/rcode/bin/rcode-tools.cjs +13 -0
- package/rcode/references/agent-shared-rules.md +40 -0
- package/rcode/references/roadmapper-playbook.md +8 -0
- package/rcode/references/sprint-checker-playbook.md +11 -0
- package/rcode/references/universal-anti-patterns.md +26 -0
- package/rcode/skills/actions/4-implementation/rcode-code-review/SKILL.md +12 -5
- package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-02-review.md +61 -10
- package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-03-triage.md +28 -3
- package/rcode/skills/actions/4-implementation/rcode-code-review/workflow.md +8 -1
- package/rcode/skills/agents/haitham-frontend/SKILL.md +1 -1
- package/rcode/skills/agents/hanzla-engineer/SKILL.md +1 -1
- package/rcode/skills/agents/hussain-pm/SKILL.md +1 -1
- package/rcode/skills/agents/noor-writer/SKILL.md +2 -2
- package/rcode/skills/agents/orchestrator/SKILL.md +35 -5
- package/rcode/skills/agents/yousef-backend/SKILL.md +2 -2
- package/rcode/skills/agents/zayd-ml/SKILL.md +1 -1
- package/rcode/skills/core/rcode-help/SKILL.md +2 -1
- package/rcode/workflows/execute-sprint.md +1 -1
- package/rcode/workflows/execute.md +5 -0
- package/rcode/workflows/new-project-research-decision.md +2 -0
- package/rcode/workflows/new-project.md +5 -0
- package/rcode/workflows/plan.md +9 -0
- package/rcode/workflows/secure-phase.md +3 -1
- package/rcode/skills/rcode-init/SKILL.md +0 -134
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzlaa/rcode",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.1",
|
|
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": {
|
|
@@ -82,6 +82,9 @@ override.
|
|
|
82
82
|
|
|
83
83
|
- **Never implement.** Dispatch to `rcode-executor`, `rcode-hanzla`, or the
|
|
84
84
|
domain specialist. If no agent fits, say so — do not fill the gap yourself.
|
|
85
|
+
- **Never stop before the first step to ask permission you already have.** If the
|
|
86
|
+
user said execute, execute. Stop AT a gate, not in front of the plan. Writing a
|
|
87
|
+
dispatch plan and handing it back is what a planner does.
|
|
85
88
|
- **Never mark work complete on an agent's say-so.** Completion comes from the
|
|
86
89
|
verification path, and a `passed` with no `falsification: upheld` is
|
|
87
90
|
self-certified.
|
|
@@ -34,6 +34,24 @@ What I need {decisions blocked on the user, or "nothing — starting now"}
|
|
|
34
34
|
- **Banner on resumed and chained runs too.** A resume is exactly when the user has
|
|
35
35
|
lost track of where things stand.
|
|
36
36
|
|
|
37
|
+
## Dispatch when told to; stop only at a real gate
|
|
38
|
+
|
|
39
|
+
"Planning never authorizes building" (see `agent-shared-rules.md`) bounds what a
|
|
40
|
+
PLANNING workflow may do. It does not apply to you when the user has asked you to
|
|
41
|
+
execute. Those are opposite failures and both are real:
|
|
42
|
+
|
|
43
|
+
| Failure | Looks like |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Planning overreaching | asked to plan, quietly starts building, ships half-finished work |
|
|
46
|
+
| Orchestrating under-reaching | told to execute end to end, writes a plan and asks "shall I start?" |
|
|
47
|
+
|
|
48
|
+
You were dispatched to run the work. **Run it.** Present and wait only when the
|
|
49
|
+
request asked for the plan itself, when a gate genuinely needs a human (a
|
|
50
|
+
checkpoint the user locked, a credential, an outward-facing action), or when
|
|
51
|
+
scope is ambiguous in a way that changes what gets built — *large* is not
|
|
52
|
+
*ambiguous*. Otherwise dispatch, and stop AT the gate rather than before the
|
|
53
|
+
first step.
|
|
54
|
+
|
|
37
55
|
## Never implement
|
|
38
56
|
|
|
39
57
|
Your job is to dispatch, monitor, checkpoint, and report. The moment you edit a
|
|
@@ -296,11 +296,13 @@ This annotation is consumed by downstream workflows (`new-project`, `progress`)
|
|
|
296
296
|
| 2. Name | 0/2 | Not started | - |
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
-
Reference
|
|
299
|
+
Reference `.rcode/templates/roadmap.md` **if present** — it is not shipped, so
|
|
300
|
+
normally it will not be. The structure above is the contract.
|
|
300
301
|
|
|
301
302
|
## STATE.md Structure
|
|
302
303
|
|
|
303
|
-
Use
|
|
304
|
+
Use `.rcode/templates/state.md` **if present** — it is not shipped, so normally
|
|
305
|
+
it will not be. The key sections below are the contract.
|
|
304
306
|
|
|
305
307
|
Key sections:
|
|
306
308
|
- Project Reference (core value, current focus)
|
|
@@ -194,6 +194,31 @@ issue:
|
|
|
194
194
|
- Single task with 10+ files
|
|
195
195
|
- Complex work (auth, payments) crammed into one plan
|
|
196
196
|
|
|
197
|
+
### Check 5b — Vertical slice, not a layer
|
|
198
|
+
|
|
199
|
+
Scope Sanity above measures plan SIZE. This measures plan SHAPE, which size
|
|
200
|
+
cannot see: a perfectly sized plan that builds one horizontal layer is still
|
|
201
|
+
wrong.
|
|
202
|
+
|
|
203
|
+
For the phase as a whole, answer: **what can someone do after this phase that
|
|
204
|
+
they could not do before?** If the honest answer is "nothing yet, later phases
|
|
205
|
+
need it" → **WARNING**, and name it as layer-first phasing.
|
|
206
|
+
|
|
207
|
+
Specific red flags:
|
|
208
|
+
|
|
209
|
+
- Schema or migration work that creates tables no task in this phase reads or
|
|
210
|
+
writes. Tables belong to the first phase that uses them, not to a phase whose
|
|
211
|
+
purpose is tables.
|
|
212
|
+
- A plan that builds a service, repository, or endpoint with no caller inside
|
|
213
|
+
the same phase and no task wiring it to one.
|
|
214
|
+
- A phase goal naming a layer or artifact type ("the API", "the data model")
|
|
215
|
+
rather than a capability.
|
|
216
|
+
|
|
217
|
+
Warning, not blocker: a genuine foundation phase (auth, migration framework) is
|
|
218
|
+
sometimes correct. But it must name what it unblocks, and be no bigger than that
|
|
219
|
+
— an unbounded foundation is the failure this check exists for. See
|
|
220
|
+
`universal-anti-patterns.md` → Layer-first phasing.
|
|
221
|
+
|
|
197
222
|
**Example issue:**
|
|
198
223
|
```yaml
|
|
199
224
|
issue:
|
|
@@ -521,6 +546,26 @@ issue:
|
|
|
521
546
|
fix_hint: "Plan was built on hallucinated findings. Re-run /rcode-debug to verify actual code state before replanning."
|
|
522
547
|
```
|
|
523
548
|
|
|
549
|
+
## Dimension 13: Plan Quality (judgment)
|
|
550
|
+
|
|
551
|
+
Dimensions 1-12 are mechanical and binary: existence, matching, exit codes,
|
|
552
|
+
acyclicity. **A plan can pass all twelve and still be bad** — a backlog with a
|
|
553
|
+
heading, tasks that are furniture, truths nobody can verify. Nothing in rcode
|
|
554
|
+
could say so until this dimension.
|
|
555
|
+
|
|
556
|
+
Load `@.rcode/agents-rules/sprint-checker/plan-quality-rubric.md` and run its
|
|
557
|
+
five dimensions: substance over furniture, done-ness clarity, coherence, scope
|
|
558
|
+
honesty, shape fit. Verdicts are strong / adequate / thin / broken, each backed
|
|
559
|
+
by a plan and task id and a quoted phrase — abstract criticism is a failure of
|
|
560
|
+
nerve and gives the planner nothing to act on.
|
|
561
|
+
|
|
562
|
+
**Blocking:** a `broken` verdict on *Done-ness clarity* or *Substance over
|
|
563
|
+
furniture*. Those two decide whether execution and verification measure anything
|
|
564
|
+
real. The other three are warnings.
|
|
565
|
+
|
|
566
|
+
Calibrate depth to the project's recorded stakes. The substance bar applies at
|
|
567
|
+
every level; the rigor bar does not.
|
|
568
|
+
|
|
524
569
|
## Dimension 12: Evidence Grounding
|
|
525
570
|
|
|
526
571
|
**Question:** Is every claim in the plan traceable to something real — a codebase grep, or an external source-of-truth document — rather than plausible-sounding invention?
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Plan Quality Rubric — the judgment pass
|
|
2
|
+
|
|
3
|
+
The twelve dimensions in `dimensions.md` are mechanical: does the file exist,
|
|
4
|
+
does the array match, does the command exit non-zero, is the graph acyclic. They
|
|
5
|
+
are necessary and they are all binary, and **a plan can pass every one of them
|
|
6
|
+
and still be bad**. Nothing in rcode could say so.
|
|
7
|
+
|
|
8
|
+
This rubric is the judgment pass. It answers a different question: not *is this
|
|
9
|
+
plan well-formed*, but *is this plan any good*.
|
|
10
|
+
|
|
11
|
+
Walk it with judgment, not as a checklist. **Be specific — cite the plan and task
|
|
12
|
+
by id, quote the phrase, name what is missing. Abstract criticism is a failure of
|
|
13
|
+
nerve**, and it is also useless: "the plan could be clearer" gives the planner
|
|
14
|
+
nothing to change.
|
|
15
|
+
|
|
16
|
+
## How to use it
|
|
17
|
+
|
|
18
|
+
1. Read the phase's plans in full before writing anything.
|
|
19
|
+
2. For each dimension, form a verdict — **strong / adequate / thin / broken** —
|
|
20
|
+
backed by specifics.
|
|
21
|
+
3. Write findings only where they add information. A `strong` dimension may need
|
|
22
|
+
none; a `broken` one needs concrete, fixable ones.
|
|
23
|
+
4. **Severity ranks impact on the plan's usefulness, not how easy the fix is.** A
|
|
24
|
+
vague phase goal is critical even though it is a one-line fix; an inconsistent
|
|
25
|
+
term might be low even though it appears in twenty places.
|
|
26
|
+
5. Calibrate to the stakes recorded at project setup. A hobby project's plan does
|
|
27
|
+
not need launch-grade rigor — but the substance bar still applies to both.
|
|
28
|
+
|
|
29
|
+
## The dimensions
|
|
30
|
+
|
|
31
|
+
### 1. Substance over furniture
|
|
32
|
+
|
|
33
|
+
Is the content earned, or is it there because the template had a slot?
|
|
34
|
+
|
|
35
|
+
- **Task theater** — tasks that exist to make the plan look thorough. "Review the
|
|
36
|
+
implementation", "ensure quality", "update documentation" with nothing named.
|
|
37
|
+
- **Verification theater** — an `<automated>` block whose commands cannot fail, or
|
|
38
|
+
that assert something already true. See Check 8a2/8a3 for the mechanical half;
|
|
39
|
+
this is the judgment half.
|
|
40
|
+
- **Evidence theater** — an `<evidence>` block citing a file that does not
|
|
41
|
+
actually support the claim.
|
|
42
|
+
|
|
43
|
+
Flag what reads like furniture **even when it is well-written furniture**.
|
|
44
|
+
|
|
45
|
+
### 2. Done-ness clarity
|
|
46
|
+
|
|
47
|
+
Would an executor reading this plan know what "done" looks like for each task?
|
|
48
|
+
|
|
49
|
+
- Every `must_haves.truth` should be a verifiable condition. "Works correctly",
|
|
50
|
+
"handles errors gracefully", "reasonable performance" — flag every one.
|
|
51
|
+
- `[DERIVED]` truths mean the requirement had no consequences recorded. A plan
|
|
52
|
+
where most truths are derived is a signal the requirements were never finished.
|
|
53
|
+
|
|
54
|
+
**Be unforgiving here.** This is the dimension execution and verification both
|
|
55
|
+
lean on hardest, and it is where a phase quietly passes against the wrong bar.
|
|
56
|
+
|
|
57
|
+
### 3. Coherence
|
|
58
|
+
|
|
59
|
+
Does the phase have a thesis, or is it a list of tasks someone wanted?
|
|
60
|
+
|
|
61
|
+
- Do the tasks serve one capability, or several unrelated ones?
|
|
62
|
+
- Does the sequencing follow from the work, or from "what is easy first"?
|
|
63
|
+
- Would removing any single task leave the phase goal unmet? If a task can be
|
|
64
|
+
dropped with no effect on the goal, ask what it is doing here.
|
|
65
|
+
|
|
66
|
+
Red flag: a phase that reads as a backlog with a heading.
|
|
67
|
+
|
|
68
|
+
### 4. Scope honesty
|
|
69
|
+
|
|
70
|
+
Are the omissions explicit, or is the reader meant to infer them?
|
|
71
|
+
|
|
72
|
+
- Is what this phase deliberately does NOT do written down?
|
|
73
|
+
- Are assumptions tagged, or silently baked into task descriptions?
|
|
74
|
+
- Was anything de-scoped quietly between the roadmap and this plan?
|
|
75
|
+
|
|
76
|
+
Count open questions and untagged assumptions against the stakes. A high count on
|
|
77
|
+
a hobby plan is fine; the same count on a plan about to be executed is a blocker.
|
|
78
|
+
|
|
79
|
+
### 5. Shape fit
|
|
80
|
+
|
|
81
|
+
Has the plan been forced into a shape that does not match the work?
|
|
82
|
+
|
|
83
|
+
- A four-task ceremony for a one-line config change is over-formalized.
|
|
84
|
+
- A single task covering an auth rewrite is under-formalized.
|
|
85
|
+
- A backend-only phase carrying UI verification steps has the wrong shape.
|
|
86
|
+
- A hobby-stakes project running the full launch pipeline will be abandoned —
|
|
87
|
+
that is a real failure, not caution.
|
|
88
|
+
|
|
89
|
+
Flag both directions. Over-formalization is the one that gets excused, and it is
|
|
90
|
+
the one that makes people stop using the process.
|
|
91
|
+
|
|
92
|
+
## Output
|
|
93
|
+
|
|
94
|
+
Append to the checker's report:
|
|
95
|
+
|
|
96
|
+
```markdown
|
|
97
|
+
## Plan Quality — {strong|adequate|thin|broken} overall
|
|
98
|
+
|
|
99
|
+
{2-3 sentences: what holds up, what is at risk. Earned by the verdicts below.}
|
|
100
|
+
|
|
101
|
+
### Substance over furniture — {verdict}
|
|
102
|
+
- **{critical|high|medium|low}** {title} (plan {N}, task {id}) — {what}. *Fix:* {what to change}.
|
|
103
|
+
|
|
104
|
+
### Done-ness clarity — {verdict}
|
|
105
|
+
...
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
A `broken` verdict on **Done-ness clarity** or **Substance** is a blocking issue:
|
|
109
|
+
those two decide whether execution and verification are measuring anything real.
|
|
110
|
+
The other three are warnings — they make the plan worse, not wrong.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customize — per-workflow and per-skill overrides that survive an update.
|
|
3
|
+
*
|
|
4
|
+
* The problem: customising rcode meant editing files under `.rcode/workflows/`,
|
|
5
|
+
* `.rcode/references/`, or `~/.claude/skills/rcode-*`. Every one of those is
|
|
6
|
+
* regenerated by the installer, so the edit worked until the next
|
|
7
|
+
* `rcode install` and then silently vanished. Users learned not to customise,
|
|
8
|
+
* or learned not to update.
|
|
9
|
+
*
|
|
10
|
+
* The fix: overrides live in `.rcode/custom/`, which the installer never writes,
|
|
11
|
+
* and are composed onto the shipped file at read time.
|
|
12
|
+
*
|
|
13
|
+
* .rcode/custom/<name>.md — team override, committed
|
|
14
|
+
* .rcode/custom/<name>.user.md — personal override, gitignored
|
|
15
|
+
*
|
|
16
|
+
* Both are APPENDED after the shipped content, in that order. Append rather than
|
|
17
|
+
* replace is deliberate: a replacing override silently drops whatever the next
|
|
18
|
+
* rcode version adds to that workflow, which is the same trap as editing the
|
|
19
|
+
* installed file, just slower to notice. If a shipped rule genuinely has to go,
|
|
20
|
+
* an override that says "ignore the X rule above, here is why" is auditable in a
|
|
21
|
+
* way a deletion never is.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
function customDir(rcodeDir) {
|
|
28
|
+
return path.join(rcodeDir, 'custom');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Layers for one name, base first. Missing files are simply absent. */
|
|
32
|
+
function layers(rcodeDir, name) {
|
|
33
|
+
const dir = customDir(rcodeDir);
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const [kind, file] of [['team', `${name}.md`], ['user', `${name}.user.md`]]) {
|
|
36
|
+
const p = path.join(dir, file);
|
|
37
|
+
if (fs.existsSync(p)) {
|
|
38
|
+
const body = fs.readFileSync(p, 'utf8').trim();
|
|
39
|
+
if (body) out.push({ kind, path: path.join('.rcode', 'custom', file), body });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolve(rcodeDir, name) {
|
|
46
|
+
if (!name) throw new Error('customize resolve requires a name (workflow or skill id)');
|
|
47
|
+
const found = layers(rcodeDir, name);
|
|
48
|
+
return {
|
|
49
|
+
ok: true,
|
|
50
|
+
name,
|
|
51
|
+
has_overrides: found.length > 0,
|
|
52
|
+
layers: found.map((l) => ({ kind: l.kind, path: l.path })),
|
|
53
|
+
// Rendered block a workflow can paste after its own content. Empty string
|
|
54
|
+
// when nothing is overridden, so callers never special-case.
|
|
55
|
+
block: found.length === 0 ? '' : [
|
|
56
|
+
'',
|
|
57
|
+
`<!-- rcode custom overrides for "${name}" — appended, not replacing -->`,
|
|
58
|
+
...found.map((l) => `\n### Override (${l.kind}) — ${l.path}\n\n${l.body}`),
|
|
59
|
+
].join('\n'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function list(rcodeDir) {
|
|
64
|
+
const dir = customDir(rcodeDir);
|
|
65
|
+
if (!fs.existsSync(dir)) return { ok: true, exists: false, overrides: [] };
|
|
66
|
+
const names = new Map();
|
|
67
|
+
for (const f of fs.readdirSync(dir)) {
|
|
68
|
+
if (!f.endsWith('.md')) continue;
|
|
69
|
+
const isUser = f.endsWith('.user.md');
|
|
70
|
+
const name = isUser ? f.slice(0, -'.user.md'.length) : f.slice(0, -'.md'.length);
|
|
71
|
+
if (!names.has(name)) names.set(name, []);
|
|
72
|
+
names.get(name).push(isUser ? 'user' : 'team');
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
ok: true,
|
|
76
|
+
exists: true,
|
|
77
|
+
overrides: [...names.entries()].map(([name, kinds]) => ({ name, kinds: kinds.sort() })),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function init(rcodeDir, name) {
|
|
82
|
+
const dir = customDir(rcodeDir);
|
|
83
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
84
|
+
const p = path.join(dir, `${name}.md`);
|
|
85
|
+
if (fs.existsSync(p)) return { ok: true, created: false, path: p };
|
|
86
|
+
fs.writeFileSync(p, `# Override: ${name}
|
|
87
|
+
|
|
88
|
+
Appended after rcode's shipped content for \`${name}\`, every run.
|
|
89
|
+
The installer never touches this file, so it survives \`rcode install\`.
|
|
90
|
+
|
|
91
|
+
Write additional rules, project conventions, or explicit carve-outs here. To
|
|
92
|
+
neutralise a shipped rule, say so and say why — that is auditable in a way
|
|
93
|
+
deleting it from the installed file is not:
|
|
94
|
+
|
|
95
|
+
> Ignore the "no files over 1000 lines" rule for \`src/generated/\` — those are
|
|
96
|
+
> machine-generated and never hand-edited.
|
|
97
|
+
`, 'utf8');
|
|
98
|
+
return { ok: true, created: true, path: p };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function dispatch(rcodeDir, args) {
|
|
102
|
+
const sub = args[0];
|
|
103
|
+
switch (sub) {
|
|
104
|
+
case 'resolve': return resolve(rcodeDir, args[1]);
|
|
105
|
+
case 'list': return list(rcodeDir);
|
|
106
|
+
case 'init': {
|
|
107
|
+
if (!args[1]) throw new Error('customize init requires a name');
|
|
108
|
+
return init(rcodeDir, args[1]);
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`Unknown customize subcommand: ${sub}. Use: resolve <name> | list | init <name>`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { dispatch, resolve, list, init };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memlog — the run's append-only memory.
|
|
3
|
+
*
|
|
4
|
+
* The problem it solves: rcode recorded decisions with `state add-decision`,
|
|
5
|
+
* manually, usually at the end of a session when someone remembered. Everything
|
|
6
|
+
* decided in between — an override, an assumption, a change of direction —
|
|
7
|
+
* existed only in the conversation, and was gone on the next `/clear` or resume.
|
|
8
|
+
* That is how a project ends up with artifacts nobody can explain and a state
|
|
9
|
+
* file that disagrees with what actually happened.
|
|
10
|
+
*
|
|
11
|
+
* The contract: one line per event, appended AS THE WORK HAPPENS, never
|
|
12
|
+
* reconstructed afterwards. Whatever is not logged is lost on resume.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately append-only and never rewritten. A log you can edit is a log you
|
|
15
|
+
* can quietly correct, which defeats the point — a wrong entry is followed by a
|
|
16
|
+
* correcting entry, so the disagreement itself stays visible.
|
|
17
|
+
*
|
|
18
|
+
* This does NOT replace state.decisions[] / ~/.rcode/decisions.jsonl. Those are
|
|
19
|
+
* the curated, queryable record of decisions that stuck. The memlog is the raw
|
|
20
|
+
* trail, including the ones that were reversed.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const TYPES = Object.freeze(['decision', 'change', 'override', 'assumption', 'event', 'blocker']);
|
|
27
|
+
const HEADER = `# Memlog
|
|
28
|
+
|
|
29
|
+
Append-only. One line per event, written as the work happens.
|
|
30
|
+
Never edit or delete a line — append a correcting entry instead, so the
|
|
31
|
+
disagreement stays visible.
|
|
32
|
+
|
|
33
|
+
| When | Type | Entry |
|
|
34
|
+
|------|------|-------|
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
function memlogPath(planningDir) {
|
|
38
|
+
return path.join(planningDir, 'MEMLOG.md');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ensureMemlog(planningDir) {
|
|
42
|
+
const p = memlogPath(planningDir);
|
|
43
|
+
if (!fs.existsSync(p)) {
|
|
44
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
45
|
+
fs.writeFileSync(p, HEADER, 'utf8');
|
|
46
|
+
return { created: true, path: p };
|
|
47
|
+
}
|
|
48
|
+
return { created: false, path: p };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Escape pipes so a free-text entry cannot break the markdown table. */
|
|
52
|
+
function cell(text) {
|
|
53
|
+
return String(text).replace(/\|/g, '\\|').replace(/\r?\n/g, ' ').trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function append(planningDir, { type, text, phase }) {
|
|
57
|
+
if (!type) throw new Error(`memlog append requires --type (${TYPES.join('|')})`);
|
|
58
|
+
if (!TYPES.includes(type)) {
|
|
59
|
+
throw new Error(`unknown memlog type "${type}" — expected one of: ${TYPES.join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
if (!text || !String(text).trim()) throw new Error('memlog append requires --text');
|
|
62
|
+
|
|
63
|
+
ensureMemlog(planningDir);
|
|
64
|
+
const p = memlogPath(planningDir);
|
|
65
|
+
const when = new Date().toISOString().replace('T', ' ').slice(0, 16);
|
|
66
|
+
const label = phase ? `${type} · phase ${phase}` : type;
|
|
67
|
+
const line = `| ${when} | ${cell(label)} | ${cell(text)} |\n`;
|
|
68
|
+
|
|
69
|
+
// appendFileSync is atomic enough for single-line appends on every platform
|
|
70
|
+
// rcode targets, and an append cannot corrupt earlier lines the way a
|
|
71
|
+
// read-modify-write can when two agents log at once.
|
|
72
|
+
fs.appendFileSync(p, line, 'utf8');
|
|
73
|
+
return { ok: true, appended: true, type, path: path.relative(path.dirname(planningDir), p) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function read(planningDir, { type, limit } = {}) {
|
|
77
|
+
const p = memlogPath(planningDir);
|
|
78
|
+
if (!fs.existsSync(p)) return { ok: true, exists: false, entries: [] };
|
|
79
|
+
const rows = fs.readFileSync(p, 'utf8').split('\n')
|
|
80
|
+
.filter((l) => l.startsWith('| 2') || /^\| \d{4}-/.test(l))
|
|
81
|
+
.map((l) => {
|
|
82
|
+
// Split on unescaped pipes only. cell() writes `\|` for a literal pipe in
|
|
83
|
+
// the text; a naive split('|') tore one entry into three columns and lost
|
|
84
|
+
// the tail. Caught by a test, not by reading the code.
|
|
85
|
+
const parts = l.split(/(?<!\\)\|/).map((c) => c.trim().replace(/\\\|/g, '|'));
|
|
86
|
+
return { when: parts[1] || '', type: parts[2] || '', text: parts[3] || '' };
|
|
87
|
+
});
|
|
88
|
+
let entries = type ? rows.filter((r) => r.type.split(' ')[0] === type) : rows;
|
|
89
|
+
if (limit) entries = entries.slice(-Number(limit));
|
|
90
|
+
return { ok: true, exists: true, total: rows.length, entries };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Unresolved assumptions and overrides — the entries that must not be forgotten
|
|
95
|
+
* at a milestone boundary. An assumption still sitting here after a whole
|
|
96
|
+
* milestone is a finding, not a formality.
|
|
97
|
+
*/
|
|
98
|
+
function open(planningDir) {
|
|
99
|
+
const all = read(planningDir);
|
|
100
|
+
if (!all.exists) return { ok: true, exists: false, open: [] };
|
|
101
|
+
const flagged = all.entries.filter((e) => {
|
|
102
|
+
const t = e.type.split(' ')[0];
|
|
103
|
+
return t === 'assumption' || t === 'override' || t === 'blocker';
|
|
104
|
+
});
|
|
105
|
+
return { ok: true, exists: true, open: flagged, count: flagged.length };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function dispatch(planningDir, args) {
|
|
109
|
+
const sub = args[0];
|
|
110
|
+
const flag = (name) => {
|
|
111
|
+
const i = args.indexOf(`--${name}`);
|
|
112
|
+
return i !== -1 ? args[i + 1] : undefined;
|
|
113
|
+
};
|
|
114
|
+
switch (sub) {
|
|
115
|
+
case 'init': return { ok: true, ...ensureMemlog(planningDir) };
|
|
116
|
+
case 'append': return append(planningDir, { type: flag('type'), text: flag('text'), phase: flag('phase') });
|
|
117
|
+
case 'read': return read(planningDir, { type: flag('type'), limit: flag('limit') });
|
|
118
|
+
case 'open': return open(planningDir);
|
|
119
|
+
default:
|
|
120
|
+
throw new Error(`Unknown memlog subcommand: ${sub}. Use: init | append | read | open`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = { dispatch, append, read, open, ensureMemlog, TYPES };
|
|
@@ -7167,6 +7167,16 @@ async function main() {
|
|
|
7167
7167
|
if (args[0] === 'list') { result = cmdPhasesList(args.slice(1)); if (result === undefined) return; }
|
|
7168
7168
|
else { console.error('Unknown phases subcommand. Valid: list'); process.exit(1); }
|
|
7169
7169
|
break;
|
|
7170
|
+
case 'customize': {
|
|
7171
|
+
const customize = require(path.join(__dirname, 'lib', 'customize.cjs'));
|
|
7172
|
+
result = customize.dispatch(RCODE_DIR, args);
|
|
7173
|
+
break;
|
|
7174
|
+
}
|
|
7175
|
+
case 'memlog': {
|
|
7176
|
+
const memlog = require(path.join(__dirname, 'lib', 'memlog.cjs'));
|
|
7177
|
+
result = memlog.dispatch(PLANNING_DIR, args);
|
|
7178
|
+
break;
|
|
7179
|
+
}
|
|
7170
7180
|
case 'find-phase':
|
|
7171
7181
|
result = cmdFindPhase(args);
|
|
7172
7182
|
break;
|
|
@@ -7615,6 +7625,9 @@ async function main() {
|
|
|
7615
7625
|
console.log(' phase scaffold-milestone --names "n1|n2|..." → bulk-create phase folders for a milestone (#731)');
|
|
7616
7626
|
console.log(' phase scaffold-all → create missing phase folders for all phases in ROADMAP.md (#731)');
|
|
7617
7627
|
console.log(' phase rename-dir <N> [--apply] → align a phase dir slug with its ROADMAP name (dry-run by default)');
|
|
7628
|
+
console.log(' customize <resolve <name>|list|init <name>> → per-workflow overrides in .rcode/custom/ that survive an update');
|
|
7629
|
+
console.log(' memlog <init|append|read|open> → append-only run memory (.planning/MEMLOG.md)');
|
|
7630
|
+
console.log(' memlog append --type <decision|change|override|assumption|event|blocker> --text "..." [--phase N]');
|
|
7618
7631
|
console.log(' workflow-config-audit → find workflows still referencing .planning/config.json (#733)');
|
|
7619
7632
|
console.log(' commit "<msg>" [--files p1 p2 ...] → atomic git commit with conventional-commits validation (no AI attribution, no --no-verify, no auto-push)');
|
|
7620
7633
|
console.log(' commit-to-subrepo --subrepo <p> "<msg>" → atomic commit inside a git subrepo (same validation as commit)');
|
|
@@ -41,6 +41,38 @@ on what it touches, so a high score is corroboration and a zero score is no
|
|
|
41
41
|
information. When your reading disagrees with the score, your reading wins, and
|
|
42
42
|
you name the file or decision that made you override.
|
|
43
43
|
|
|
44
|
+
**Apply the project's overrides before you follow a shipped rule.** rcode's
|
|
45
|
+
workflows and references are regenerated by the installer, so anything a project
|
|
46
|
+
edited into them is lost on the next update. Overrides live in `.rcode/custom/`,
|
|
47
|
+
which the installer never writes:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node ".rcode/bin/rcode-tools.cjs" customize resolve <workflow-or-reference-name>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Non-empty `block` is appended AFTER the shipped content and wins where they
|
|
54
|
+
conflict. Overrides append rather than replace on purpose: a replacing override
|
|
55
|
+
silently drops whatever the next rcode version adds, which is the same trap as
|
|
56
|
+
editing the installed file, just slower to notice.
|
|
57
|
+
|
|
58
|
+
**Log it when it happens, not when you remember.** Every decision, change of
|
|
59
|
+
direction, override, and assumption goes into the memlog at the moment it occurs:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
node ".rcode/bin/rcode-tools.cjs" memlog append \
|
|
63
|
+
--type <decision|change|override|assumption|event|blocker> \
|
|
64
|
+
--text "<one line, with the reason>" [--phase N]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Not at the end of the session, not "when there's a natural pause" — those are the
|
|
68
|
+
entries that never get written. **Whatever is not logged is lost on the next
|
|
69
|
+
`/clear` or resume**, and a project whose artifacts nobody can explain is the
|
|
70
|
+
result. The memlog is append-only: a wrong entry is followed by a correcting
|
|
71
|
+
entry, never edited away, so the disagreement stays visible.
|
|
72
|
+
|
|
73
|
+
This does not replace `state add-decision` — that is the curated record of
|
|
74
|
+
decisions that stuck. The memlog is the raw trail, including the reversals.
|
|
75
|
+
|
|
44
76
|
**Elicitation is not authoring — hand the pen back.** When gathering what the user
|
|
45
77
|
wants, the moment you catch yourself naming the stack, picking the MVP cut, or
|
|
46
78
|
proposing the phase breakdown, stop and hand it back to them. Infer-and-confirm
|
|
@@ -77,6 +109,14 @@ If you believe the next step is obvious and valuable, say so in one line and sto
|
|
|
77
109
|
The user typing the next command takes two seconds; undoing an unrequested build
|
|
78
110
|
took a whole session.
|
|
79
111
|
|
|
112
|
+
**This rule bounds a workflow's scope; it does not make every agent timid.** It
|
|
113
|
+
exists because planning workflows slid into building and produced half-finished
|
|
114
|
+
work nobody asked for. It says nothing about an orchestrator that was explicitly
|
|
115
|
+
told to execute. When the user says "execute this end to end", dispatching IS the
|
|
116
|
+
requested scope, and handing back a plan with "shall I start?" is the same defect
|
|
117
|
+
pointed the other way — a question they already answered. See the orchestrator
|
|
118
|
+
contract for where that line sits.
|
|
119
|
+
|
|
80
120
|
---
|
|
81
121
|
|
|
82
122
|
## Redirect protocol
|
|
@@ -156,6 +156,14 @@ Read only when the current task needs the detail. Don't preemptively load.
|
|
|
156
156
|
**Read PROJECT.md's Glossary and use its terms verbatim** in phase names and
|
|
157
157
|
goals. A roadmap that renames the domain's nouns forces every downstream
|
|
158
158
|
agent to guess which concept a phase is about.
|
|
159
|
+
**Cut phases vertically.** Every phase must answer: what can someone do after
|
|
160
|
+
this that they could not do before? A phase whose goal names a layer ("the
|
|
161
|
+
data model", "the API", "all the repositories") rather than a capability is
|
|
162
|
+
horizontal, and everything it builds goes unexercised until some later phase
|
|
163
|
+
reaches for it — which is how a service ships with no caller. Create the
|
|
164
|
+
schema, services, and endpoints a phase's own capability needs, and no more.
|
|
165
|
+
A genuine foundation phase is allowed, but it names what it unblocks in the
|
|
166
|
+
same sentence and is no bigger than that.
|
|
159
167
|
**Read PROJECT.md's Out of Scope (Non-Goals) before phasing.** A phase whose
|
|
160
168
|
goal reaches into a declared non-goal is scope creep with a plan attached —
|
|
161
169
|
flag it rather than quietly phasing it.
|
|
@@ -140,3 +140,14 @@ Read these only when actually performing the check. Don't preemptively load.
|
|
|
140
140
|
- Never modify sprints — read-only analysis
|
|
141
141
|
- Produce CHECK.md at `.planning/phases/{phase}/{phase}-{sprint}-CHECK.md`
|
|
142
142
|
- Block execution on critical fails (missing coverage, broken deps, unverifiable outcomes)
|
|
143
|
+
|
|
144
|
+
## The judgment pass
|
|
145
|
+
|
|
146
|
+
Dimensions 1-12 tell you whether a plan is well-formed. They cannot tell you
|
|
147
|
+
whether it is any good — a plan of well-written furniture passes all of them.
|
|
148
|
+
|
|
149
|
+
Dimension 13 loads `@.rcode/agents-rules/sprint-checker/plan-quality-rubric.md`
|
|
150
|
+
and answers that second question. Run it on every phase; scale its depth to the
|
|
151
|
+
project's stakes, never skip it. A `broken` verdict on done-ness clarity or on
|
|
152
|
+
substance blocks, because those two decide whether everything downstream is
|
|
153
|
+
measuring anything real.
|
|
@@ -10,6 +10,32 @@ Phase plan picks up scope adjacent to the actual goal. Symptom: phase descriptio
|
|
|
10
10
|
### Implicit prerequisites
|
|
11
11
|
Phase assumes another phase has shipped without declaring the dependency. Symptom: plan refers to a file or table that doesn't exist yet. Fix: surface the dependency in the phase's `Depends on` line in ROADMAP.md.
|
|
12
12
|
|
|
13
|
+
### Layer-first phasing (horizontal slices)
|
|
14
|
+
|
|
15
|
+
A phase whose whole job is one technical layer: "Phase 1 — create all the
|
|
16
|
+
database tables", "Phase 2 — build every repository", "Phase 3 — the API".
|
|
17
|
+
Symptom: the phase's goal names a layer or an artifact type rather than
|
|
18
|
+
something a user can do afterwards, and no phase before the last one produces
|
|
19
|
+
anything anybody can use.
|
|
20
|
+
|
|
21
|
+
Why it costs more than it looks: nothing in a layer-first phase is exercised
|
|
22
|
+
until a much later phase reaches for it, so a table, a service, or an endpoint
|
|
23
|
+
can be built wrong — or built and never wired to anything — and pass every gate
|
|
24
|
+
in between. Confirmed live: a project shipped a cycle-closing service with
|
|
25
|
+
exactly one importer in the whole repo, its own test, because the phase that
|
|
26
|
+
built it was never obliged to connect it to anything a user touches.
|
|
27
|
+
|
|
28
|
+
Fix: **cut phases vertically.** Each phase delivers one thing end to end, and
|
|
29
|
+
creates only the schema, services, and endpoints that thing needs. Tables get
|
|
30
|
+
created by the first phase that reads or writes them, not by a phase whose
|
|
31
|
+
purpose is tables. If a foundation genuinely must come first (auth, a
|
|
32
|
+
migration framework), name what it unblocks in the same sentence and keep it as
|
|
33
|
+
small as that.
|
|
34
|
+
|
|
35
|
+
The test: read a phase goal and ask *what can someone do after this that they
|
|
36
|
+
could not do before?* If the honest answer is "nothing yet, but later phases
|
|
37
|
+
need it", the phase is horizontal.
|
|
38
|
+
|
|
13
39
|
### Vague acceptance
|
|
14
40
|
Acceptance criterion is "users can do X" with no measurable threshold. Fix: make every acceptance criterion observable from outside the system — a CLI command, an API response, a log line, a UI assertion.
|
|
15
41
|
|
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
name: rcode-review
|
|
3
3
|
internal: true
|
|
4
4
|
description: >
|
|
5
|
-
Review code changes
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
Review code changes from seven parallel angles — three that gather evidence
|
|
6
|
+
(cold scan, what was removed, call-path trace) and four that judge (reuse,
|
|
7
|
+
simplification, efficiency, altitude) — then verify every finding
|
|
8
|
+
adversarially before reporting, so false positives never reach the user. Activates when the user says "review this PR", "review
|
|
9
|
+
this code", "run code review", "do a PR review", "review the diff", "review
|
|
10
|
+
this branch", "critique this implementation", or "CR" — including when a
|
|
11
|
+
GitHub PR URL or a PR number is given with no other wording ("review
|
|
12
|
+
https://github.com/org/repo/pull/792", "review #792"). Do NOT use for
|
|
13
|
+
documentation review (use rcode-validate-prd or editorial skills).
|
|
10
14
|
triggers:
|
|
11
15
|
# English
|
|
12
16
|
- "review this code"
|
|
13
17
|
- "run code review"
|
|
14
18
|
- "do a PR review"
|
|
19
|
+
- "review this PR"
|
|
20
|
+
- "review the PR"
|
|
21
|
+
- "review this branch"
|
|
15
22
|
- "review the diff"
|
|
16
23
|
- "critique this implementation"
|
|
17
24
|
- "find bad code practices"
|