ralphctl 0.25.0 → 0.27.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.
Files changed (59) hide show
  1. package/README.md +11 -5
  2. package/THIRD-PARTY-NOTICES.md +65 -2
  3. package/dist/agent-definitions/ralphctl-generator.md +3 -2
  4. package/dist/{cli-GQBXYNFN.mjs → cli-EYDXERW6.mjs} +9423 -6665
  5. package/dist/cli.mjs +1 -1
  6. package/dist/manifest.json +20 -3
  7. package/dist/prompts/_partials/approval-gate.md +5 -5
  8. package/dist/prompts/_partials/autonomous-operation.md +23 -6
  9. package/dist/prompts/_partials/conventions-agents-md.md +8 -20
  10. package/dist/prompts/_partials/conventions-claude-md.md +12 -15
  11. package/dist/prompts/_partials/conventions-copilot-instructions.md +6 -10
  12. package/dist/prompts/_partials/decisions.md +3 -9
  13. package/dist/prompts/_partials/evaluator-failure-modes.md +11 -5
  14. package/dist/prompts/_partials/evaluator-grading-rules.md +25 -10
  15. package/dist/prompts/_partials/evidence-bound.md +2 -2
  16. package/dist/prompts/_partials/git-boundary.md +7 -0
  17. package/dist/prompts/_partials/harness-context.md +1 -1
  18. package/dist/prompts/_partials/one-shot-operation.md +6 -0
  19. package/dist/prompts/_partials/question-format.md +7 -0
  20. package/dist/prompts/_partials/skill-body.md +5 -0
  21. package/dist/prompts/_partials/task-blocked.md +10 -0
  22. package/dist/prompts/_partials/task-fields.md +19 -3
  23. package/dist/prompts/_partials/task-sizing.md +2 -2
  24. package/dist/prompts/_partials/validation-checklist.md +6 -8
  25. package/dist/prompts/apply-feedback/template.md +24 -25
  26. package/dist/prompts/create-pr/template.md +21 -15
  27. package/dist/prompts/detect-scripts/template.md +10 -9
  28. package/dist/prompts/detect-skills/template.md +19 -23
  29. package/dist/prompts/distill-learnings/template.md +10 -8
  30. package/dist/prompts/evaluate/template.md +74 -93
  31. package/dist/prompts/evaluate-continuation/template.md +19 -22
  32. package/dist/prompts/ideate/template.md +36 -47
  33. package/dist/prompts/implement/template.md +59 -73
  34. package/dist/prompts/implement-continuation/template.md +21 -20
  35. package/dist/prompts/implement-crash-resume/template.md +13 -4
  36. package/dist/prompts/plan/template.md +75 -64
  37. package/dist/prompts/readiness/template.md +36 -27
  38. package/dist/prompts/refine/template.md +37 -50
  39. package/dist/prompts/reproduce/template.md +9 -7
  40. package/dist/prompts/select-candidate/template.md +17 -10
  41. package/dist/skills/ralphctl-api-and-interface-design/SKILL.md +117 -0
  42. package/dist/skills/ralphctl-code-review-and-quality/SKILL.md +119 -8
  43. package/dist/skills/ralphctl-code-simplification/SKILL.md +100 -0
  44. package/dist/skills/ralphctl-codebase-design/SKILL.md +82 -0
  45. package/dist/skills/ralphctl-debugging-and-error-recovery/SKILL.md +23 -5
  46. package/dist/skills/ralphctl-deprecation-and-migration/SKILL.md +106 -0
  47. package/dist/skills/ralphctl-frontend-ui-engineering/SKILL.md +94 -0
  48. package/dist/skills/ralphctl-humanizer/SKILL.md +137 -0
  49. package/dist/skills/ralphctl-idea-refinement/SKILL.md +2 -0
  50. package/dist/skills/ralphctl-iterative-review/SKILL.md +10 -4
  51. package/dist/skills/ralphctl-observability-and-instrumentation/SKILL.md +104 -0
  52. package/dist/skills/ralphctl-performance-optimization/SKILL.md +123 -0
  53. package/dist/skills/ralphctl-ponytail/SKILL.md +38 -28
  54. package/dist/skills/ralphctl-security-and-hardening/SKILL.md +259 -0
  55. package/dist/skills/ralphctl-source-driven-development/SKILL.md +103 -0
  56. package/dist/skills/ralphctl-spec-driven-development/SKILL.md +148 -0
  57. package/dist/skills/ralphctl-test-driven-development/SKILL.md +29 -2
  58. package/dist/skills/ralphctl-writing-for-agents/SKILL.md +113 -0
  59. package/package.json +10 -10
@@ -35,11 +35,11 @@ be a correct, complete, maintainable solution. Write exactly one `candidate-sele
35
35
  </task_specification>
36
36
 
37
37
  <candidate_1>
38
- {{CANDIDATE_A_SUMMARY}}
38
+ {{CANDIDATE_1_SUMMARY}}
39
39
  </candidate_1>
40
40
 
41
41
  <candidate_2>
42
- {{CANDIDATE_B_SUMMARY}}
42
+ {{CANDIDATE_2_SUMMARY}}
43
43
  </candidate_2>
44
44
 
45
45
  <constraints>
@@ -47,11 +47,18 @@ be a correct, complete, maintainable solution. Write exactly one `candidate-sele
47
47
  - Judge only from `<candidate_1>` and `<candidate_2>` above — do not attempt to read either candidate's
48
48
  actual diff, branch, or working tree, even if you have the technical means to. The two summaries are
49
49
  the entire evidence base by design.
50
- - Weigh a cited verification outcome (a command that was run, a test that passed or failed, an endpoint
51
- that was exercised) above an unverified narrative claim of completion.
52
- - A summary that reports its own verification as failed or partial is not automatically the loser — a
53
- candidate that honestly reports a partial result and is closer to the acceptance criteria can still beat
54
- one that claims full success without evidence.
50
+ - Each summary has the same fields. `Verify outcome` and `Diff` are measured by the harness: the outcome is
51
+ the result of the project's verify script on that candidate's tree (`success`, `failed`, `skipped` when no
52
+ verify script is configured, or `spawn-error` when the script could not start), followed in parentheses by
53
+ an attribution against the pre-task baseline — `clean` (passed and was passing before), `regressed`
54
+ (passed before, fails now), `baseline-broken` (was already failing and still is), `fixed-baseline` (was
55
+ failing, now passes). A green script shows existing checks pass, not that the task's criteria are met.
56
+ `Proposed commit`, `Changes claimed` (the last 5) and `Notes` (the last 3) are the candidate's own words.
57
+ - Weigh a measured verify outcome, and a command or test result a candidate cites, above
58
+ an unverified narrative claim of completion.
59
+ - A candidate whose verify outcome failed is not automatically the loser — one that failed on a baseline
60
+ problem, or that is closer to the acceptance criteria, can still beat one that claims full success
61
+ without evidence.
55
62
  - When both summaries are similarly strong on verification, prefer the one whose changed-files list stays
56
63
  closest to the task's declared scope.
57
64
  - When the two are genuinely indistinguishable on the evidence given, declare a tie rather than inventing
@@ -70,9 +77,9 @@ evidence base. The only file you write is `signals.json`, to the output director
70
77
 
71
78
  ### Phase 1 — Read both summaries
72
79
 
73
- Read `<task_specification>`, then both candidate summaries in full before forming any preliminary
74
- judgment. Note, for each candidate: what it attempted, what its verification outcome claims, which files
75
- it touched, and any other notable signal worth weighing (warnings, notes, learnings).
80
+ Read `<task_specification>` and both candidate summaries in full before forming any
81
+ judgment, weighing for each candidate what it attempted, its verify outcome, which files it touched, and
82
+ its notes and claimed changes.
76
83
 
77
84
  ### Phase 2 — Compare against the task
78
85
 
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: ralphctl-api-and-interface-design
3
+ description: Interface-design discipline for any surface one piece of code exposes to another — network endpoints, library exports, module boundaries, CLI flags and exit codes, file and message formats. Contract first, one consistent error model, validation at the trust boundary, additive change over breaking change, and every observable behaviour treated as a commitment (Hyrum's law). Use when designing a new interface, changing an existing one, or planning work that crosses a module or service boundary.
4
+ license: MIT
5
+ ---
6
+
7
+ # API and Interface Design
8
+
9
+ > Adapted from [addyosmani/agent-skills — "API and Interface Design"](https://github.com/addyosmani/agent-skills)
10
+ > (MIT). Adapted for ralphctl's harness contract.
11
+
12
+ Good interfaces make the right use easy and the wrong use hard. This applies to every surface where one
13
+ piece of code talks to another: a network API, a library's exports, a module boundary inside one
14
+ repository, a CLI's flags and exit codes, a file or message format. The guidance is language-neutral —
15
+ express it in the project's own idiom and with its own typing or schema mechanism.
16
+
17
+ ## When this applies
18
+
19
+ - **Plan** — when a ticket adds or changes an interface, make the contract its own task or the first step
20
+ of one, and put its shape in the verification criteria: inputs, outputs, error cases, and what existing
21
+ consumers must keep seeing. Name who consumes the interface today.
22
+ - **Execute** — define the contract before the body, and check every change to an existing interface
23
+ against its current consumers before making it. When reviewing, a breaking change the task did not ask
24
+ for is a finding even when the new code is correct.
25
+
26
+ ## Every observable behaviour is a commitment
27
+
28
+ Hyrum's law: with enough consumers, everything observable gets depended on — field order, error message
29
+ text, default values, timing, undocumented quirks. So:
30
+
31
+ - Expose deliberately. Anything a caller can observe is something you may have to keep.
32
+ - Keep implementation details out of the interface — storage ids, internal layout, stack traces in errors.
33
+ - Tests are necessary but not sufficient: a "safe" change still breaks a consumer that relies on behaviour
34
+ no test pins. Search for real consumers before changing what they can see.
35
+
36
+ ## Core principles
37
+
38
+ ### Contract first
39
+
40
+ Write the interface — names, input and output shapes, error cases, invariants — before the
41
+ implementation. The contract is the spec; the body follows it. Separate what a caller provides from what
42
+ the system returns: generated fields (ids, timestamps) appear only in the output shape.
43
+
44
+ ### One error model, everywhere
45
+
46
+ Pick one way to report failure and use it on every operation: the same envelope or error type, a stable
47
+ machine-readable code, a human-readable message, optional detail. Map failure classes consistently —
48
+ invalid input, not authenticated, not permitted, not found, conflict, internal. When some operations
49
+ throw, others return an empty value, and others return an error object, the caller cannot predict
50
+ behaviour. Internal failures never expose internals.
51
+
52
+ ### Validate at the boundary, trust inside
53
+
54
+ Validate where untrusted data enters: request handlers, argument parsing, config and environment loading,
55
+ responses from third-party services, files the code did not write. After validation, internal code
56
+ trusts the types and does not re-validate. Third-party responses are untrusted — check shape and content
57
+ before using them in logic, rendering, or decisions. For the full trust-boundary treatment, see
58
+ ralphctl-security-and-hardening when installed.
59
+
60
+ ### Prefer addition over modification
61
+
62
+ - Add optional fields, new operations, and new variants a consumer can ignore.
63
+ - Do not change a field's type, remove or rename a field, or narrow what was previously accepted.
64
+ - When a breaking change is unavoidable, treat it as a migration (ralphctl-deprecation-and-migration when
65
+ installed), not an in-place edit.
66
+ - Extend one live version rather than forking a second one consumers must choose between.
67
+
68
+ ### Predictable names and shapes
69
+
70
+ Follow the conventions the project already uses for this kind of surface, identically across every
71
+ operation: casing, resource naming, boolean prefixes, time and id formats. Model variants as an explicit
72
+ tagged union or discriminated record rather than a bag of optional fields. Where the language allows,
73
+ give each kind of id its own type so one cannot be passed for another. List operations paginate (a limit
74
+ plus a stable order or cursor) from the start; partial updates change only the fields provided.
75
+
76
+ ### Retry safety
77
+
78
+ A state-changing operation either honours an idempotency key or is documented as unsafe to retry. When
79
+ it honours one:
80
+
81
+ - Derive the key from the intent (client-supplied, or an immutable id of the thing being done), never per
82
+ attempt — a fresh random value or a timestamp makes every retry a new effect.
83
+ - Claim the key atomically — a uniqueness constraint or a single compare-and-set. A check followed by an
84
+ act is a race.
85
+ - Reject the same key arriving with a different payload instead of replaying the first response.
86
+ - Decide on purpose what an in-flight duplicate gets: rejected, a bounded wait, or a pending status.
87
+ - A timeout is a third outcome — unknown. Record the intent before the side effect so it can be resolved.
88
+ - Keep keys longer than the longest path that can re-deliver the same request.
89
+
90
+ ## Common rationalisations
91
+
92
+ | Rationalisation | Reality |
93
+ | ------------------------------------------ | ----------------------------------------------------------------------------------------- |
94
+ | "Nobody uses that undocumented behaviour" | If it is observable, someone depends on it. Treat it as a commitment. |
95
+ | "We'll pin the contract down later" | The contract is the documentation. Define it first; the implementation follows it. |
96
+ | "Internal interfaces don't need contracts" | Internal consumers are still consumers. Contracts are what let them change independently. |
97
+ | "We don't need pagination yet" | Adding it once a list has grown is a breaking change. |
98
+ | "Accepting the idempotency key is enough" | Accepting it tells clients retries are safe; storing it against the result makes it so. |
99
+
100
+ ## Red flags
101
+
102
+ - An operation that returns different shapes depending on conditions.
103
+ - Inconsistent error formats across one interface.
104
+ - Validation scattered through internal code instead of at the boundary.
105
+ - A field changed, renamed, or removed in place.
106
+ - A list operation without pagination.
107
+ - Third-party responses used without validation.
108
+ - A lookup-then-insert idempotency check, or a key regenerated on every attempt.
109
+
110
+ ## Self-check
111
+
112
+ - [ ] Every operation has explicit input and output shapes and named error cases.
113
+ - [ ] One error model across the whole interface.
114
+ - [ ] Validation happens at the boundary only.
115
+ - [ ] Changes to an existing interface are additive, or handled as a migration.
116
+ - [ ] Naming matches the conventions the project already uses.
117
+ - [ ] The retry behaviour of every state-changing operation is a deliberate choice.
@@ -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 | Reality |
228
- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
229
- | "It works, that's good enough" | Working code that is unreadable, insecure, or architecturally wrong creates debt that compounds. |
230
- | "I wrote it, so I know it is correct" | Authors are blind to their own assumptions. |
231
- | "We will clean it up later" | Later rarely comes. The review is the quality gate — use it. |
232
- | "AI-generated code is probably fine" | AI code needs more scrutiny, not less. It is confident and plausible, even when wrong. |
233
- | "The tests pass, so it is good" | Tests are necessary but not sufficient. They do not catch architecture problems, security issues, or readability concerns. |
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
- > Adapted for ralphctl's harness contract.
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–10 wrong.
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.