ralphctl 0.26.0 → 0.28.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 +11 -4
- package/THIRD-PARTY-NOTICES.md +65 -2
- package/dist/agent-definitions/ralphctl-generator.md +3 -2
- package/dist/{cli-P7D6G5NI.mjs → chunk-WAHQPVTG.mjs} +16782 -40661
- package/dist/cli-W3M7L4TP.mjs +2772 -0
- package/dist/cli.mjs +1 -1
- package/dist/launch-VBRTQP4L.mjs +20602 -0
- package/dist/manifest.json +16 -3
- package/dist/prompts/_partials/autonomous-operation.md +18 -7
- package/dist/prompts/_partials/conventions-agents-md.md +2 -2
- package/dist/prompts/_partials/conventions-claude-md.md +2 -0
- package/dist/prompts/_partials/decisions.md +0 -1
- package/dist/prompts/_partials/evaluator-failure-modes.md +8 -5
- package/dist/prompts/_partials/evaluator-grading-rules.md +9 -5
- package/dist/prompts/_partials/one-shot-operation.md +6 -0
- package/dist/prompts/apply-feedback/template.md +5 -4
- package/dist/prompts/create-pr/template.md +10 -8
- package/dist/prompts/detect-scripts/template.md +7 -4
- package/dist/prompts/detect-skills/template.md +2 -0
- package/dist/prompts/evaluate/template.md +4 -2
- package/dist/prompts/evaluate-continuation/template.md +1 -1
- package/dist/prompts/ideate/template.md +2 -3
- package/dist/prompts/implement/template.md +21 -14
- package/dist/prompts/plan/template.md +3 -4
- package/dist/prompts/readiness/template.md +9 -4
- package/dist/prompts/refine/template.md +3 -6
- package/dist/prompts/reproduce/template.md +7 -0
- package/dist/skills/ralphctl-api-and-interface-design/SKILL.md +117 -0
- package/dist/skills/ralphctl-code-review-and-quality/SKILL.md +119 -8
- package/dist/skills/ralphctl-code-simplification/SKILL.md +100 -0
- package/dist/skills/ralphctl-codebase-design/SKILL.md +82 -0
- package/dist/skills/ralphctl-debugging-and-error-recovery/SKILL.md +23 -5
- package/dist/skills/ralphctl-deprecation-and-migration/SKILL.md +106 -0
- package/dist/skills/ralphctl-frontend-ui-engineering/SKILL.md +94 -0
- package/dist/skills/ralphctl-humanizer/SKILL.md +137 -0
- package/dist/skills/ralphctl-idea-refinement/SKILL.md +2 -0
- package/dist/skills/ralphctl-iterative-review/SKILL.md +10 -4
- package/dist/skills/ralphctl-observability-and-instrumentation/SKILL.md +104 -0
- package/dist/skills/ralphctl-performance-optimization/SKILL.md +123 -0
- package/dist/skills/ralphctl-ponytail/SKILL.md +38 -28
- package/dist/skills/ralphctl-security-and-hardening/SKILL.md +259 -0
- package/dist/skills/ralphctl-source-driven-development/SKILL.md +103 -0
- package/dist/skills/ralphctl-spec-driven-development/SKILL.md +148 -0
- package/dist/skills/ralphctl-test-driven-development/SKILL.md +29 -2
- package/dist/skills/ralphctl-writing-for-agents/SKILL.md +113 -0
- package/package.json +10 -10
|
@@ -7,7 +7,9 @@ license: MIT
|
|
|
7
7
|
# Code Review and Quality
|
|
8
8
|
|
|
9
9
|
> Concept from [addyosmani/agent-skills — "Code Review and Quality"](https://github.com/addyosmani/agent-skills),
|
|
10
|
-
> MIT License. Adapted for ralphctl's evaluator role and review flow.
|
|
10
|
+
> MIT License. Adapted for ralphctl's evaluator role and review flow. The smell baseline draws on
|
|
11
|
+
> [mattpocock/skills](https://github.com/mattpocock/skills) and the concrete-case and expected-load rules on
|
|
12
|
+
> [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) (both MIT).
|
|
11
13
|
|
|
12
14
|
One-shot generation looks fast and is slow. Catching a correctness, architecture, or security problem at the
|
|
13
15
|
seam between two changes is cheap; catching it at the end of a 200-line diff — or after the post-task gate
|
|
@@ -57,6 +59,10 @@ Can another engineer understand this code without the author explaining it?
|
|
|
57
59
|
- Could this be done in fewer lines? (1 000 lines where 100 suffice is a failure.)
|
|
58
60
|
- Are abstractions earning their complexity? Do not generalise until the third use case.
|
|
59
61
|
- Are there dead code artefacts: no-op variables, backwards-compat shims, or `// removed` comments?
|
|
62
|
+
- **Is a new conditional bolted onto an unrelated flow?** That is a design smell, not a nit — push the logic
|
|
63
|
+
into its own helper, state, or policy instead of tangling an existing path.
|
|
64
|
+
- **Do repeated conditionals on the same shape appear?** They signal a missing model or dispatcher. A
|
|
65
|
+
"temporary" branch is usually permanent debt.
|
|
60
66
|
|
|
61
67
|
### 3. Architecture
|
|
62
68
|
|
|
@@ -67,6 +73,16 @@ Does the change fit the system's design?
|
|
|
67
73
|
- Is there code duplication that should be shared?
|
|
68
74
|
- Are dependencies flowing in the right direction — no circular dependencies?
|
|
69
75
|
- Is the abstraction level appropriate — not over-engineered, not too coupled?
|
|
76
|
+
- **Does this refactor reduce complexity or just relocate it?** Count the concepts a reader must hold to
|
|
77
|
+
follow the change. If a "cleaner" version leaves that count unchanged, it is not cleaner — prefer the
|
|
78
|
+
restructuring that makes whole branches, modes, or layers disappear over one that re-centralises the same
|
|
79
|
+
logic. Prefer deleting an abstraction to polishing it.
|
|
80
|
+
- **Is feature-specific logic leaking into a shared or general-purpose module?** Keep logic in its owning
|
|
81
|
+
layer, reuse the existing canonical helper instead of a near-duplicate, and do not normalise architectural
|
|
82
|
+
drift.
|
|
83
|
+
- **Are type boundaries explicit?** Question gratuitous untyped escape hatches, optional fields, casts, and
|
|
84
|
+
silent fallbacks that paper over an unclear invariant — making the boundary explicit often makes the
|
|
85
|
+
surrounding control flow simpler.
|
|
70
86
|
|
|
71
87
|
### 4. Security
|
|
72
88
|
|
|
@@ -91,6 +107,47 @@ Does the change introduce performance problems?
|
|
|
91
107
|
- Any missing pagination on list endpoints?
|
|
92
108
|
- Any large objects created in hot paths?
|
|
93
109
|
|
|
110
|
+
Judge scale against the expected load, found in the repository (README, deploy configuration): a script one
|
|
111
|
+
person runs is not a service under concurrent traffic. Check-then-write races, work every process repeats,
|
|
112
|
+
collections that only grow, and per-process state that must be shared are findings at the load that
|
|
113
|
+
triggers them — say which load you assumed.
|
|
114
|
+
|
|
115
|
+
## Structural Remedies
|
|
116
|
+
|
|
117
|
+
When you flag a structural problem, propose the move — not just the problem. A finding that only says "this
|
|
118
|
+
is complex" leaves the generator or author guessing. Reach for a named restructuring:
|
|
119
|
+
|
|
120
|
+
- **Replace a chain of conditionals** with a typed model or an explicit dispatcher.
|
|
121
|
+
- **Collapse duplicate branches** into a single clearer flow.
|
|
122
|
+
- **Separate orchestration from business logic** so each reads on its own.
|
|
123
|
+
- **Move feature-specific logic** out of a shared module into the module that owns the concept.
|
|
124
|
+
- **Reuse the canonical helper** instead of a bespoke near-duplicate.
|
|
125
|
+
- **Make a type boundary explicit** so downstream branching disappears.
|
|
126
|
+
- **Delete a pass-through wrapper** that adds indirection without clarifying the API.
|
|
127
|
+
- **Extract a helper, or split a large file** into focused modules.
|
|
128
|
+
|
|
129
|
+
Prefer the remedy that removes moving pieces over one that spreads the same complexity around.
|
|
130
|
+
|
|
131
|
+
## Smell Baseline
|
|
132
|
+
|
|
133
|
+
On top of whatever the project documents, a fixed set of code smells (Fowler, _Refactoring_) applies even
|
|
134
|
+
where a repository documents nothing. Each is a labelled judgement call ("possible Feature Envy") at Minor or
|
|
135
|
+
Nit, never a hard violation. A documented project standard that endorses the pattern overrides the smell, and
|
|
136
|
+
anything tooling already enforces is skipped.
|
|
137
|
+
|
|
138
|
+
- **Mysterious Name** — the name does not say what it does or holds → rename; no honest name means murky design.
|
|
139
|
+
- **Duplicated Code** — the same logic shape in several hunks or files → extract it once.
|
|
140
|
+
- **Feature Envy** — a function that works on another module's data more than its own → move it there.
|
|
141
|
+
- **Data Clumps** — the same few values always travel together → give them one type.
|
|
142
|
+
- **Primitive Obsession** — a string or number standing in for a domain concept → give the concept a type.
|
|
143
|
+
- **Repeated Switches** — the same branching on the same type in several places → one dispatch both share.
|
|
144
|
+
- **Shotgun Surgery** — one logical change forces scattered edits → gather what changes together.
|
|
145
|
+
- **Divergent Change** — one module edited for several unrelated reasons → split by reason.
|
|
146
|
+
- **Speculative Generality** — hooks, parameters, or abstractions for needs nobody has → delete them.
|
|
147
|
+
- **Message Chains** — callers walking a long chain of navigation → hide the walk behind one call.
|
|
148
|
+
- **Middle Man** — a layer that mostly delegates → call the real target directly.
|
|
149
|
+
- **Refused Bequest** — an implementer ignoring most of what it inherits → compose instead of inheriting.
|
|
150
|
+
|
|
94
151
|
## Severity Vocabulary
|
|
95
152
|
|
|
96
153
|
Label every finding with its severity so the generator or author knows what is required versus optional:
|
|
@@ -105,6 +162,12 @@ Label every finding with its severity so the generator or author knows what is r
|
|
|
105
162
|
Using explicit severity prevents treating all findings as equally urgent — a nit should not consume the
|
|
106
163
|
same budget as a Critical.
|
|
107
164
|
|
|
165
|
+
**Presumptive structural findings.** Raise each of these as at least Minor and propose the simpler design;
|
|
166
|
+
escalate to Major only when the change actively makes the structure worse: a refactor that relocates
|
|
167
|
+
complexity instead of reducing it; a change that pushes a file past a healthy size with no decomposition;
|
|
168
|
+
feature logic added to a shared module; a near-duplicate of an existing canonical helper; a silent fallback
|
|
169
|
+
that hides an unclear invariant.
|
|
170
|
+
|
|
108
171
|
## Review Process
|
|
109
172
|
|
|
110
173
|
### Step 1: Understand the Context
|
|
@@ -124,6 +187,12 @@ Tests reveal intent and coverage:
|
|
|
124
187
|
- Are edge cases covered?
|
|
125
188
|
- Would the tests catch a regression if the code changed?
|
|
126
189
|
|
|
190
|
+
Answer the last question by experiment, not by reading, when your session may edit the working tree (the
|
|
191
|
+
generator): invert one condition the change adds (drop a negation, swap `&&` for `||`), run the focused
|
|
192
|
+
test, then restore the file from a copy. A mutation that stays green is a finding — name the missing test
|
|
193
|
+
case. When your session is read-only (the evaluator), reason through the same inversion and name the test
|
|
194
|
+
case that would be missing as a finding.
|
|
195
|
+
|
|
127
196
|
### Step 3: Review the Implementation
|
|
128
197
|
|
|
129
198
|
Walk through the code with the five axes in mind. For each file changed:
|
|
@@ -153,6 +222,11 @@ section names the exact file and shape):
|
|
|
153
222
|
the severity of each finding) in the evaluator's output as directed by the task prompt — not in a
|
|
154
223
|
separate file or report, and not as a `decision` signal.
|
|
155
224
|
|
|
225
|
+
**Lead with what matters.** Order findings by leverage: correctness and security first, then structural
|
|
226
|
+
regressions and missed simplifications, then everything else. Do not bury a real issue under cosmetic nits —
|
|
227
|
+
a few high-conviction findings beat a long list. If you have one structural problem and ten nits, the
|
|
228
|
+
structural problem _is_ the review.
|
|
229
|
+
|
|
156
230
|
Do not write a standalone review report to a file. The harness's signal pipeline and the evaluator's
|
|
157
231
|
structured output are the authoritative record.
|
|
158
232
|
|
|
@@ -178,6 +252,11 @@ Small, focused changes are easier to review, faster to evaluate, and safer to fo
|
|
|
178
252
|
~1000 lines changed → Too large. Split it.
|
|
179
253
|
```
|
|
180
254
|
|
|
255
|
+
**Watch file size, not just diff size.** A small diff can still push a file past a healthy boundary —
|
|
256
|
+
around 1 000 _total_ lines in a single file (distinct from the ~1 000 _changed_-lines threshold above) is a
|
|
257
|
+
common inspection signal, not a hard cap. When a change materially grows an already-large file, ask whether
|
|
258
|
+
to extract helpers, subcomponents, or modules _first_, before piling more on. Decompose, then add.
|
|
259
|
+
|
|
181
260
|
**What counts as "one change":** A single self-contained modification that addresses one thing, includes
|
|
182
261
|
related tests, and keeps the system functional after submission.
|
|
183
262
|
|
|
@@ -208,11 +287,30 @@ Before adding any dependency:
|
|
|
208
287
|
|
|
209
288
|
Prefer the standard library and existing utilities over new dependencies. Every dependency is a liability.
|
|
210
289
|
|
|
290
|
+
**Upgrading an existing dependency** is a code change like any other, and the riskiest upgrades are bulk
|
|
291
|
+
"bump deps" changes. Review them with the same discipline:
|
|
292
|
+
|
|
293
|
+
- **Read the changelog, not just the version number.** Semver is a promise the maintainer may not have
|
|
294
|
+
kept — a "patch" can carry a behavioural change. For a major bump, read the migration notes and find what
|
|
295
|
+
breaks.
|
|
296
|
+
- **One dependency per change** (or a small related group). When a bulk bump breaks the build, you lose which
|
|
297
|
+
package did it; a single-package change makes the cause obvious and the revert clean.
|
|
298
|
+
- **Let the tests decide.** The upgrade is verified by a green check before _and_ after, not by "it
|
|
299
|
+
installed." If coverage around the dependency's behaviour is thin, that gap is the real finding — add a
|
|
300
|
+
test first.
|
|
301
|
+
- **Mind the transitive graph.** Review the lockfile diff, not just the dependency manifest; a single direct
|
|
302
|
+
bump can pull in dozens of indirect changes.
|
|
303
|
+
- **Keep the lockfile honest.** Keep it in the change, review its diff, and never hand-edit it — the lockfile
|
|
304
|
+
is what actually pins what ships.
|
|
305
|
+
|
|
211
306
|
## Honesty in Review
|
|
212
307
|
|
|
213
308
|
Whether reviewing code you wrote, code a generator produced, or a human's change:
|
|
214
309
|
|
|
215
310
|
- Do not rubber-stamp. "Looks fine" without evidence of review helps no one.
|
|
311
|
+
- Back every correctness, security, or scale finding with a concrete case — this input or situation leads to
|
|
312
|
+
this wrong result — and re-read the lines to confirm it: the caller exists, the value can really be empty,
|
|
313
|
+
the code really is unused. No case, no finding. Structural findings follow the presumptive rules above.
|
|
216
314
|
- Do not soften real issues. "This might be a minor concern" when it is a bug that will hit production is
|
|
217
315
|
misleading.
|
|
218
316
|
- Quantify problems when possible. "This N+1 query will add ~50 ms per item in the list" is better than
|
|
@@ -224,13 +322,17 @@ Whether reviewing code you wrote, code a generator produced, or a human's change
|
|
|
224
322
|
|
|
225
323
|
## Common Rationalisations
|
|
226
324
|
|
|
227
|
-
| Rationalisation
|
|
228
|
-
|
|
|
229
|
-
| "It works, that's good enough"
|
|
230
|
-
| "I wrote it, so I know it is correct"
|
|
231
|
-
| "We will clean it up later"
|
|
232
|
-
| "AI-generated code is probably fine"
|
|
233
|
-
| "The tests pass, so it is good"
|
|
325
|
+
| Rationalisation | Reality |
|
|
326
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
327
|
+
| "It works, that's good enough" | Working code that is unreadable, insecure, or architecturally wrong creates debt that compounds. |
|
|
328
|
+
| "I wrote it, so I know it is correct" | Authors are blind to their own assumptions. |
|
|
329
|
+
| "We will clean it up later" | Later rarely comes. The review is the quality gate — use it. |
|
|
330
|
+
| "AI-generated code is probably fine" | AI code needs more scrutiny, not less. It is confident and plausible, even when wrong. |
|
|
331
|
+
| "The tests pass, so it is good" | Tests are necessary but not sufficient. They do not catch architecture problems, security issues, or readability concerns. |
|
|
332
|
+
| "The refactor makes it cleaner" | Relocating complexity is not reducing it. If the reader still holds the same number of concepts, the structure did not improve — look for the version where branches disappear. |
|
|
333
|
+
| "It is only a small addition to this file" | Small diffs still push files past a healthy size and bolt branches onto unrelated flows. Judge the resulting structure, not the diff size. |
|
|
334
|
+
| "It is just a version bump" | A bump is a behaviour change you did not write. Read the changelog; semver does not guarantee no breakage. |
|
|
335
|
+
| "Upgrade everything at once to save time" | A bulk bump that breaks the build hides which package did it. One dependency per change keeps the cause and the revert clean. |
|
|
234
336
|
|
|
235
337
|
## Review Checklist
|
|
236
338
|
|
|
@@ -243,6 +345,9 @@ Before signalling completion or a passing evaluator verdict, run through:
|
|
|
243
345
|
- [ ] Names are clear and consistent with project conventions.
|
|
244
346
|
- [ ] No unnecessary complexity.
|
|
245
347
|
- [ ] Follows existing architectural patterns.
|
|
348
|
+
- [ ] Refactors reduce complexity rather than relocate it.
|
|
349
|
+
- [ ] No feature logic in shared modules; touched files stay within a healthy size.
|
|
350
|
+
- [ ] Dependency upgrades (if any) were checked against their changelog and the lockfile diff.
|
|
246
351
|
- [ ] No secrets in code; input validated at boundaries.
|
|
247
352
|
- [ ] No N+1 patterns or unbounded operations.
|
|
248
353
|
- [ ] Findings surfaced per Step 4's signal guidance, with severity labels intact.
|
|
@@ -255,3 +360,9 @@ Before signalling completion or a passing evaluator verdict, run through:
|
|
|
255
360
|
- No regression tests alongside a bug fix.
|
|
256
361
|
- Review comments without severity labels — makes it unclear what is required versus optional.
|
|
257
362
|
- Accepting "I will fix it later" — it rarely happens.
|
|
363
|
+
- A refactor that moves code around without reducing the number of concepts a reader must hold.
|
|
364
|
+
- A change that grows an already-large file instead of decomposing it.
|
|
365
|
+
- New conditionals scattered into unrelated code paths (a missing abstraction).
|
|
366
|
+
- A bespoke helper that duplicates an existing canonical one, or feature logic placed in a shared module.
|
|
367
|
+
- A bulk dependency bump with no changelog review and no per-package isolation.
|
|
368
|
+
- A lockfile change that is hand-edited or accepted without reviewing its diff.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralphctl-code-simplification
|
|
3
|
+
description: Behaviour-preserving simplification of existing code — understand why the code exists before changing it, then reduce nesting, unclear names, duplication, dead code, and needless indirection one step at a time with the existing tests unchanged and green after each. Use when a task asks to refactor or clean up working code, or when code you just made work is heavier than it needs to be; for keeping a new change small from the start, see ralphctl-surgical-simplicity and ralphctl-ponytail when installed.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Simplification
|
|
8
|
+
|
|
9
|
+
> Adapted from [addyosmani/agent-skills — "Code Simplification"](https://github.com/addyosmani/agent-skills)
|
|
10
|
+
> (MIT). Adapted for ralphctl's harness contract.
|
|
11
|
+
|
|
12
|
+
Simplify by reducing complexity while preserving behaviour exactly. The goal is not fewer lines — it is
|
|
13
|
+
code a new reader understands faster than the original. Every simplification passes one test: would
|
|
14
|
+
someone new to this code follow the result more easily?
|
|
15
|
+
|
|
16
|
+
## When this applies
|
|
17
|
+
|
|
18
|
+
- **Execute** — when the task asks for a refactor or cleanup, and after a change works, on the code that
|
|
19
|
+
change touched. Simplification outside the task's scope is a finding to surface, not an edit to make —
|
|
20
|
+
record it in a `note` signal when the prompt's output-contract section lists it; otherwise fold it into
|
|
21
|
+
the required signal's own fields.
|
|
22
|
+
|
|
23
|
+
**When not to use:** the code is already clear; you do not yet understand what it does; the simpler form
|
|
24
|
+
is measurably slower on a hot path; the module is about to be replaced.
|
|
25
|
+
|
|
26
|
+
## Principles
|
|
27
|
+
|
|
28
|
+
1. **Preserve behaviour exactly.** Same outputs for every input, same errors, same side effects and their
|
|
29
|
+
order. The existing tests pass without modification. If you are not sure a change preserves
|
|
30
|
+
behaviour, do not make it.
|
|
31
|
+
2. **Follow the project's conventions.** Read the project's context file when present and the
|
|
32
|
+
neighbouring code; simplification that breaks local consistency is churn.
|
|
33
|
+
3. **Clarity over cleverness.** A chain of nested conditional expressions becomes early returns or a
|
|
34
|
+
lookup; a dense chained transform with inline logic becomes a named intermediate step.
|
|
35
|
+
4. **Keep the balance.** Do not inline a helper that gives a concept its name, merge unrelated logic into
|
|
36
|
+
one function, or remove an abstraction that exists for testability. Line count is not the measure.
|
|
37
|
+
5. **Scope to what changed.** Default to the code the task touched.
|
|
38
|
+
|
|
39
|
+
## The process
|
|
40
|
+
|
|
41
|
+
### 1. Understand before touching (Chesterton's fence)
|
|
42
|
+
|
|
43
|
+
Before changing or removing anything, know why it exists: its responsibility, its callers and callees,
|
|
44
|
+
its edge cases and error paths, the tests that pin its behaviour, and why it might have been written this
|
|
45
|
+
way (a performance constraint, a platform quirk, history visible in `git log` or `git blame`). If you
|
|
46
|
+
cannot answer these, read more first.
|
|
47
|
+
|
|
48
|
+
### 2. Find concrete opportunities
|
|
49
|
+
|
|
50
|
+
| Pattern | Simplification |
|
|
51
|
+
| ------------------------------------ | ----------------------------------------------------- |
|
|
52
|
+
| Nesting three or more levels deep | Guard clauses or an extracted, well-named condition |
|
|
53
|
+
| A long function doing several jobs | Split by job, never by line count |
|
|
54
|
+
| Boolean flag parameters | Separate functions, or a named options value |
|
|
55
|
+
| The same condition in several places | One named predicate |
|
|
56
|
+
| Generic or misleading names | Names that describe the content or the real behaviour |
|
|
57
|
+
| A comment explaining _what_ | Delete it; keep comments that explain _why_ |
|
|
58
|
+
| Duplicated logic | One shared function |
|
|
59
|
+
| Dead code | Remove it, after confirming nothing reaches it |
|
|
60
|
+
| A wrapper that only forwards | Call the underlying function directly |
|
|
61
|
+
| An abstraction with one variant | The direct approach |
|
|
62
|
+
|
|
63
|
+
### 3. Apply one change at a time
|
|
64
|
+
|
|
65
|
+
Make one simplification, run the focused tests, and continue only when they are green; when they fail,
|
|
66
|
+
undo that step and reconsider. Never batch several simplifications into one untested change — when
|
|
67
|
+
something breaks, you need to know which one did it. Keep refactoring and behaviour change apart: a
|
|
68
|
+
refactor task adds no behaviour, and a feature task does not absorb a broad refactor. For a change
|
|
69
|
+
touching many sites, prefer a mechanical transform (a structured search-and-replace or a codemod the
|
|
70
|
+
project already uses) over hand edits.
|
|
71
|
+
|
|
72
|
+
### 4. Judge the whole
|
|
73
|
+
|
|
74
|
+
Compare before and after: is the result genuinely easier to follow, consistent with the codebase, and a
|
|
75
|
+
clean reviewable diff? If not, undo it. Not every attempt succeeds.
|
|
76
|
+
|
|
77
|
+
## Common rationalisations
|
|
78
|
+
|
|
79
|
+
| Rationalisation | Reality |
|
|
80
|
+
| ---------------------------------------- | --------------------------------------------------------------------------------- |
|
|
81
|
+
| "Fewer lines is always simpler" | A one-line nested conditional is not simpler than five readable lines. |
|
|
82
|
+
| "I'll quickly tidy this unrelated code" | Unscoped cleanup inflates the diff and risks regressions you did not intend. |
|
|
83
|
+
| "This abstraction might be useful later" | Speculative abstractions are complexity without value. Remove it; re-add on need. |
|
|
84
|
+
| "The tests needed a small tweak" | A simplification that needs test changes probably changed behaviour. |
|
|
85
|
+
|
|
86
|
+
## Red flags
|
|
87
|
+
|
|
88
|
+
- Tests modified to make a "simplification" pass.
|
|
89
|
+
- A "simplified" version that is longer or harder to follow.
|
|
90
|
+
- Renames that follow your preference rather than the project's conventions.
|
|
91
|
+
- Error handling removed because it "looked noisy".
|
|
92
|
+
- Simplifying code you do not fully understand.
|
|
93
|
+
|
|
94
|
+
## Self-check
|
|
95
|
+
|
|
96
|
+
- [ ] The existing tests pass without modification.
|
|
97
|
+
- [ ] No error handling was removed or weakened.
|
|
98
|
+
- [ ] The result follows the project's conventions.
|
|
99
|
+
- [ ] No dead code was left behind (unused imports, unreachable branches).
|
|
100
|
+
- [ ] The diff stays inside the task's scope.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralphctl-codebase-design
|
|
3
|
+
description: Shared vocabulary and tests for module design — deep modules (much behaviour behind a small interface), seams placed where behaviour actually varies, the deletion test for pass-through layers, and the interface as the test surface. Use when planning module boundaries, deciding where a seam or abstraction belongs, judging whether a layer earns its keep, or making code easier to test through its interface.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Codebase Design
|
|
8
|
+
|
|
9
|
+
> Adapted from [mattpocock/skills — "Codebase Design"](https://github.com/mattpocock/skills) (MIT).
|
|
10
|
+
> Adapted for ralphctl's harness contract.
|
|
11
|
+
|
|
12
|
+
Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable
|
|
13
|
+
through that interface. The aim is leverage for callers, locality for maintainers, and testability for
|
|
14
|
+
everyone. Reason with the vocabulary below; in code and in signals, keep the project's own terms when it
|
|
15
|
+
has established ones.
|
|
16
|
+
|
|
17
|
+
## When this applies
|
|
18
|
+
|
|
19
|
+
- **Plan** — when splitting work, prefer tasks that deepen one module behind a small interface over tasks
|
|
20
|
+
that spread one concept across several shallow ones. When a task introduces a seam, name it in the
|
|
21
|
+
steps along with what varies across it.
|
|
22
|
+
- **Execute** — whenever you add a module, an interface, or an abstraction. When reviewing, apply the
|
|
23
|
+
deletion test and the one-adapter rule below to every new layer.
|
|
24
|
+
|
|
25
|
+
## Vocabulary
|
|
26
|
+
|
|
27
|
+
- **Module** — anything with an interface and an implementation, at any scale: a function, a type, a
|
|
28
|
+
package, a slice spanning several layers.
|
|
29
|
+
- **Interface** — everything a caller must know to use the module correctly: the type signature, plus its
|
|
30
|
+
invariants, ordering constraints, error modes, required configuration, and performance characteristics.
|
|
31
|
+
- **Implementation** — the code inside the module.
|
|
32
|
+
- **Depth** — leverage at the interface: how much behaviour a caller or test can exercise per unit of
|
|
33
|
+
interface it has to learn. **Deep** when much sits behind little; **shallow** when the interface is
|
|
34
|
+
nearly as complex as what it hides.
|
|
35
|
+
- **Seam** (Michael Feathers) — a place where behaviour can be altered without editing in that place; the
|
|
36
|
+
location where a module's interface lives. Where to put the seam is its own decision.
|
|
37
|
+
- **Adapter** — a concrete thing that satisfies an interface at a seam. It names a role, not a size.
|
|
38
|
+
- **Leverage** — what callers gain from depth: more capability per thing they learn.
|
|
39
|
+
- **Locality** — what maintainers gain from depth: change, bugs, and knowledge concentrated in one place.
|
|
40
|
+
Fix once, fixed everywhere.
|
|
41
|
+
|
|
42
|
+
## Principles
|
|
43
|
+
|
|
44
|
+
- **Depth is a property of the interface.** A deep module can be built from small, swappable internal
|
|
45
|
+
parts; they are just not part of its interface. It may have internal seams for its own tests as well as
|
|
46
|
+
the external seam callers use.
|
|
47
|
+
- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If
|
|
48
|
+
the complexity reappears across many callers, it was earning its keep.
|
|
49
|
+
- **The interface is the test surface.** Callers and tests cross the same seam. Needing to test past the
|
|
50
|
+
interface means the module is the wrong shape.
|
|
51
|
+
- **One adapter is a hypothetical seam; two adapters is a real one.** Introduce a seam only where
|
|
52
|
+
something actually varies across it — a second implementation, or a test fake standing in for slow or
|
|
53
|
+
non-deterministic infrastructure.
|
|
54
|
+
|
|
55
|
+
When designing an interface, ask: can it have fewer operations, simpler parameters, more hidden inside?
|
|
56
|
+
Before committing to one shape, sketch two or three genuinely different interfaces for the same job and
|
|
57
|
+
compare them on depth, locality, and where the seam falls.
|
|
58
|
+
|
|
59
|
+
## Designing for testability
|
|
60
|
+
|
|
61
|
+
- **Accept dependencies instead of creating them.** A function that receives its payment gateway or
|
|
62
|
+
clock can be tested with a fake; one that constructs the real thing internally cannot.
|
|
63
|
+
- **Return results instead of producing side effects.** Computing a discount and returning it is testable
|
|
64
|
+
by assertion; mutating a shared total in place is testable only by inspection.
|
|
65
|
+
- **Keep the surface small.** Fewer operations mean fewer tests; fewer parameters mean simpler setup.
|
|
66
|
+
|
|
67
|
+
## Rejected framings
|
|
68
|
+
|
|
69
|
+
- **Depth as a ratio of implementation lines to interface lines** — it rewards padding the implementation.
|
|
70
|
+
Depth here means leverage.
|
|
71
|
+
- **"Interface" as only a language keyword or a list of public methods** — too narrow; every fact a caller
|
|
72
|
+
must know is part of it.
|
|
73
|
+
- **"Boundary"** for a seam — overloaded with domain-driven design's bounded context. Say seam or
|
|
74
|
+
interface.
|
|
75
|
+
|
|
76
|
+
## Red flags
|
|
77
|
+
|
|
78
|
+
- A module whose interface mirrors its implementation one-to-one.
|
|
79
|
+
- A wrapper that only forwards calls.
|
|
80
|
+
- An interface with exactly one implementation and no test fake — a seam nothing varies across.
|
|
81
|
+
- A test that reaches past the interface into internals.
|
|
82
|
+
- One concept spread across several shallow modules that must always change together.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ralphctl-debugging-and-error-recovery
|
|
3
|
-
description: Systematic root-cause debugging. Use when tests fail, builds break, or behaviour does not match expectations. Follow stop-the-line → reproduce → localize → reduce → root-cause → guard-with-regression-test → verify, not guessing; the reproduction and regression steps follow the same red-green discipline as ralphctl-test-driven-development.
|
|
3
|
+
description: Systematic root-cause debugging. Use when tests fail, builds break, something that worked before broke, or behaviour does not match expectations — when you need to figure out what broke and why. Follow stop-the-line → reproduce → localize → reduce → root-cause → guard-with-regression-test → verify, not guessing; the reproduction and regression steps follow the same red-green discipline as ralphctl-test-driven-development.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Debugging and Error Recovery
|
|
8
8
|
|
|
9
|
-
> Adapted from [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) (MIT)
|
|
10
|
-
>
|
|
9
|
+
> Adapted from [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) (MIT), with the
|
|
10
|
+
> feedback-loop, ranked-hypothesis, and tagged-log practices drawn from
|
|
11
|
+
> [mattpocock/skills](https://github.com/mattpocock/skills) (MIT). Adapted for ralphctl's harness contract.
|
|
11
12
|
|
|
12
13
|
Systematic debugging with structured triage. When something breaks, stop adding features, preserve
|
|
13
14
|
evidence, and follow a structured process to find and fix the root cause. Guessing wastes time. The
|
|
@@ -32,7 +33,7 @@ When anything unexpected happens:
|
|
|
32
33
|
6. **Resume** only after the fix is verified with the project's narrow check gate.
|
|
33
34
|
|
|
34
35
|
Do not push past a failing test or broken build to work on the next feature. Errors compound — a
|
|
35
|
-
bug at Step 3 that goes unfixed makes Steps 4–
|
|
36
|
+
bug at Step 3 that goes unfixed makes Steps 4–6 wrong.
|
|
36
37
|
|
|
37
38
|
## The Triage Checklist
|
|
38
39
|
|
|
@@ -42,6 +43,15 @@ Work through these steps in order. Do not skip steps.
|
|
|
42
43
|
|
|
43
44
|
Make the failure happen reliably. If you cannot reproduce it, you cannot fix it with confidence.
|
|
44
45
|
|
|
46
|
+
**Build a tight, red-capable loop before forming any theory.** When your session can run commands, the
|
|
47
|
+
first deliverable is one command you have already run that drives the real code path and asserts the
|
|
48
|
+
exact reported symptom — a failing test at the seam that reaches the bug, a script with a fixture input, a
|
|
49
|
+
captured payload replayed through the code, the same input run through the old and new versions. Tight
|
|
50
|
+
means fast (seconds) and deterministic, or for a flaky bug a pinned, high reproduction rate. Reading code
|
|
51
|
+
to build a theory before that command exists is the failure this step prevents. When no loop can be built
|
|
52
|
+
in this session, list what you tried and what would make it possible rather than guessing — in a `note`
|
|
53
|
+
signal when the prompt's output-contract section lists it, otherwise in the required signal's own fields.
|
|
54
|
+
|
|
45
55
|
When a bug is non-reproducible, work through these branches:
|
|
46
56
|
|
|
47
57
|
- **Timing-dependent** — add timestamps to logs near the suspected area; try with artificial delays to widen race windows; run under load or concurrency to increase collision probability.
|
|
@@ -79,7 +89,12 @@ Create the minimal failing case:
|
|
|
79
89
|
|
|
80
90
|
A minimal reproduction makes the root cause obvious and prevents fixing symptoms instead of causes.
|
|
81
91
|
|
|
82
|
-
### Step 4: Fix the Root Cause
|
|
92
|
+
### Step 4: Hypothesise, then Fix the Root Cause
|
|
93
|
+
|
|
94
|
+
Before testing any explanation, write down three to five ranked hypotheses — a single one anchors on the
|
|
95
|
+
first plausible idea. Each must be falsifiable: "if X is the cause, changing Y makes the symptom disappear".
|
|
96
|
+
A hypothesis with no prediction is a hunch; sharpen it or drop it. Test them in rank order against the
|
|
97
|
+
loop, changing one variable at a time.
|
|
83
98
|
|
|
84
99
|
Fix the underlying issue, not the symptom.
|
|
85
100
|
|
|
@@ -149,6 +164,9 @@ when the prompt's output-contract section lists it, otherwise in the required si
|
|
|
149
164
|
|
|
150
165
|
Add logging only when it helps. Remove it when done.
|
|
151
166
|
|
|
167
|
+
- **Tag temporary logs** with one unique prefix (for example `[DEBUG-a4f2]`), so removal is a single
|
|
168
|
+
search. Untagged temporary logs survive into the diff; search for the prefix and remove every hit
|
|
169
|
+
before you signal the work complete.
|
|
152
170
|
- **When to add** — you cannot localize the failure to a specific line; the issue is intermittent; the fix involves multiple interacting components.
|
|
153
171
|
- **When to remove** — the bug is fixed and a regression test guards against recurrence; the log is only useful during development.
|
|
154
172
|
- **Permanent instrumentation (keep)** — error boundaries with error reporting; API error logging with request context; performance metrics at key user flows.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralphctl-deprecation-and-migration
|
|
3
|
+
description: Discipline for removing or replacing code, interfaces, stored data shapes, and dependencies safely — confirm the replacement covers every consumer, migrate consumers incrementally behind an adapter or switch, change persisted data in additive expand-then-contract phases with a tested way back, and remove the old path only once nothing uses it. Use when retiring an API, module, feature, flag, or dependency, renaming or dropping a stored field, or moving callers from one implementation to another.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Deprecation and Migration
|
|
8
|
+
|
|
9
|
+
> Adapted from [addyosmani/agent-skills — "Deprecation and Migration"](https://github.com/addyosmani/agent-skills)
|
|
10
|
+
> (MIT). Adapted for ralphctl's harness contract.
|
|
11
|
+
|
|
12
|
+
Code is a liability, not an asset: every line needs tests, upgrades, and attention from anyone working
|
|
13
|
+
nearby. Deprecation removes code that no longer earns its keep; migration moves its consumers safely to
|
|
14
|
+
what replaces it. Most teams are good at adding things and bad at removing them — this skill is the
|
|
15
|
+
removal half.
|
|
16
|
+
|
|
17
|
+
## When this applies
|
|
18
|
+
|
|
19
|
+
- **Plan** — sequence the work so each step ships on its own: the replacement first, consumers next,
|
|
20
|
+
removal last and alone. Give the removal task the verification criterion "no remaining consumers", and
|
|
21
|
+
keep each data-shape phase below a separate task.
|
|
22
|
+
- **Execute** — implement one phase per task. Do not fold an additive step and its destructive follow-up
|
|
23
|
+
into one change, and do not remove anything a consumer still reaches.
|
|
24
|
+
|
|
25
|
+
## Before removing anything
|
|
26
|
+
|
|
27
|
+
Hyrum's law makes removal hard: consumers depend on behaviour the replacement may not reproduce —
|
|
28
|
+
ordering, defaults, error text, side effects. Answer first:
|
|
29
|
+
|
|
30
|
+
1. Does the old thing still provide something nothing else does? If so, keep it.
|
|
31
|
+
2. Who consumes it? Search code, configuration, scripts, docs, and tests, including dynamic lookups by
|
|
32
|
+
name. Consumers outside this repository may exist — say so rather than assume none.
|
|
33
|
+
3. Does a replacement exist and cover every critical use? If not, build that first.
|
|
34
|
+
4. What does each consumer's move cost, against the ongoing cost of keeping both?
|
|
35
|
+
|
|
36
|
+
## The process
|
|
37
|
+
|
|
38
|
+
1. **Replacement first.** It covers the critical cases and is proven, not just "theoretically better".
|
|
39
|
+
2. **Mark the old path deprecated** with the project's own mechanism — a deprecation annotation, a
|
|
40
|
+
runtime warning, a changelog entry — when it has one. Do not invent a new notice file.
|
|
41
|
+
3. **Migrate incrementally.** One consumer or area per change: find its touchpoints, switch it, confirm
|
|
42
|
+
behaviour matches with its tests, remove its reference to the old path. Whoever retires a thing
|
|
43
|
+
migrates its consumers or ships a compatible shim — never leave them to discover the break.
|
|
44
|
+
4. **Remove only at zero.** Once no consumer remains: the code, its tests, docs, configuration, and the
|
|
45
|
+
deprecation markers themselves.
|
|
46
|
+
|
|
47
|
+
## Migration patterns
|
|
48
|
+
|
|
49
|
+
- **Strangler** — run old and new side by side and route a growing share to the new path; remove the old
|
|
50
|
+
one when its share is zero.
|
|
51
|
+
- **Adapter** — keep the old interface as a thin layer over the new implementation, so consumers keep
|
|
52
|
+
working while the backend moves.
|
|
53
|
+
- **Switch per consumer** — cut consumers over one at a time with the project's existing flag or
|
|
54
|
+
configuration mechanism.
|
|
55
|
+
|
|
56
|
+
## Persisted data: expand, then contract
|
|
57
|
+
|
|
58
|
+
A stored shape is the riskiest thing to change — reverting the code does not revert the data, and during a
|
|
59
|
+
rollout old and new code run against the same store. Never change a stored shape in place:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
EXPAND → MIGRATE → CONTRACT
|
|
63
|
+
add the new shape write both, backfill old records, stop writing the old shape,
|
|
64
|
+
beside the old one then switch reads to the new one then drop it in a later change
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- Additive first, destructive last and alone. Adds are safe in any release; drops and renames ship on
|
|
68
|
+
their own, after nothing reads the old shape.
|
|
69
|
+
- Every migration has a tested way back — a down migration, or readers that tolerate both shapes.
|
|
70
|
+
- A migration is safe to re-run and safe to interrupt; stamp what has been migrated.
|
|
71
|
+
- Backfill in batches, off the hot path, so it does not lock or starve the store.
|
|
72
|
+
- The same rules hold for files on disk: version the format, and keep readers tolerant of the old shape.
|
|
73
|
+
|
|
74
|
+
## Zombie code
|
|
75
|
+
|
|
76
|
+
Code nobody owns but something still depends on — no maintainer, failing tests nobody fixes, outdated
|
|
77
|
+
dependencies. Either give it an owner and maintain it, or deprecate it with a concrete migration. It
|
|
78
|
+
cannot stay in limbo.
|
|
79
|
+
|
|
80
|
+
## Common rationalisations
|
|
81
|
+
|
|
82
|
+
| Rationalisation | Reality |
|
|
83
|
+
| ------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
84
|
+
| "It still works, why remove it?" | Unmaintained working code accumulates risk and complexity silently. |
|
|
85
|
+
| "Someone might need it later" | It can be rebuilt if needed. Keeping it "just in case" costs more. |
|
|
86
|
+
| "Consumers will migrate on their own" | They will not. Migrate them, or ship a shim that needs no migration. |
|
|
87
|
+
| "Just rename the field, it's one line" | During rollout, old and new code run together and one of them reads a field that is gone. |
|
|
88
|
+
| "Add the new field and drop the old one together" | That couples a safe add to a destructive drop. Drops ship alone, later. |
|
|
89
|
+
| "We'll write the way back if we need it" | A migration with no way back is a release you cannot undo. |
|
|
90
|
+
|
|
91
|
+
## Red flags
|
|
92
|
+
|
|
93
|
+
- A deprecation with no working replacement.
|
|
94
|
+
- Removal without a search for remaining consumers.
|
|
95
|
+
- New features added to the deprecated path.
|
|
96
|
+
- A stored field renamed or dropped in place.
|
|
97
|
+
- A schema change and the code that depends on it shipped as one step.
|
|
98
|
+
- A migration with no way back, or a backfill that locks the store.
|
|
99
|
+
|
|
100
|
+
## Self-check
|
|
101
|
+
|
|
102
|
+
- [ ] The replacement covers every critical use of the old path.
|
|
103
|
+
- [ ] Every known consumer is migrated; unknown external consumers are named as a risk.
|
|
104
|
+
- [ ] Persisted-shape changes are phased: expand, migrate, contract.
|
|
105
|
+
- [ ] Each migration has a tested way back and is safe to re-run.
|
|
106
|
+
- [ ] Removal deletes the code, its tests, docs, configuration, and deprecation markers.
|