@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.
Files changed (35) hide show
  1. package/cli/install.js +93 -6
  2. package/cli/uninstall.js +13 -0
  3. package/dist/rcode.js +195 -195
  4. package/package.json +1 -1
  5. package/rcode/agents/rcode-orchestrator.md +3 -0
  6. package/rcode/agents/rules/orchestrator/contract.md +18 -0
  7. package/rcode/agents/rules/roadmapper/detailed-guide.md +4 -2
  8. package/rcode/agents/rules/sprint-checker/dimensions.md +45 -0
  9. package/rcode/agents/rules/sprint-checker/plan-quality-rubric.md +110 -0
  10. package/rcode/bin/lib/customize.cjs +115 -0
  11. package/rcode/bin/lib/memlog.cjs +124 -0
  12. package/rcode/bin/rcode-tools.cjs +13 -0
  13. package/rcode/references/agent-shared-rules.md +40 -0
  14. package/rcode/references/roadmapper-playbook.md +8 -0
  15. package/rcode/references/sprint-checker-playbook.md +11 -0
  16. package/rcode/references/universal-anti-patterns.md +26 -0
  17. package/rcode/skills/actions/4-implementation/rcode-code-review/SKILL.md +12 -5
  18. package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-02-review.md +61 -10
  19. package/rcode/skills/actions/4-implementation/rcode-code-review/steps/step-03-triage.md +28 -3
  20. package/rcode/skills/actions/4-implementation/rcode-code-review/workflow.md +8 -1
  21. package/rcode/skills/agents/haitham-frontend/SKILL.md +1 -1
  22. package/rcode/skills/agents/hanzla-engineer/SKILL.md +1 -1
  23. package/rcode/skills/agents/hussain-pm/SKILL.md +1 -1
  24. package/rcode/skills/agents/noor-writer/SKILL.md +2 -2
  25. package/rcode/skills/agents/orchestrator/SKILL.md +35 -5
  26. package/rcode/skills/agents/yousef-backend/SKILL.md +2 -2
  27. package/rcode/skills/agents/zayd-ml/SKILL.md +1 -1
  28. package/rcode/skills/core/rcode-help/SKILL.md +2 -1
  29. package/rcode/workflows/execute-sprint.md +1 -1
  30. package/rcode/workflows/execute.md +5 -0
  31. package/rcode/workflows/new-project-research-decision.md +2 -0
  32. package/rcode/workflows/new-project.md +5 -0
  33. package/rcode/workflows/plan.md +9 -0
  34. package/rcode/workflows/secure-phase.md +3 -1
  35. 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.14.0",
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 full template: `.rcode/templates/roadmap.md`
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 template from `.rcode/templates/state.md`.
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 using parallel adversarial review layers and produce
6
- actionable findings. Activates when the user says "review this code", "run
7
- code review", "do a PR review", "review the diff", "critique this
8
- implementation", or "CR". Do NOT use for documentation review (use
9
- rcode-validate-prd or editorial skills).
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"