From predicate
Evaluates code for structural simplicity and complected concerns using Rich Hickey's Simple Made Easy framework. Invoke via /hickey to detect accidental complexity.
How this skill is triggered — by the user, by Claude, or both
Slash command
/predicate:hickeyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Evaluate code for **structural simplicity** using Rich Hickey's "Simple Made Easy" framework. Designed for a world where AI generates code faster than humans can read it — where functional tests pass but accidental complexity accumulates silently.
Evaluate code for structural simplicity using Rich Hickey's "Simple Made Easy" framework. Designed for a world where AI generates code faster than humans can read it — where functional tests pass but accidental complexity accumulates silently.
The core premise: tests tell you code works; they tell you nothing about whether it's simple. Hickey: "What's true of every bug found in the field? It passed the type checker... it passed all the tests." Complected code can be perfectly correct today. The damage surfaces when you try to change it, reason about it, or extend it.
[!NOTE] This workflow is the spatial half of the code-spacetime review pair. It reads code as a static snapshot — what's tangled right now? For the temporal half — what will pull apart, and when? — see
/lowy. Run both for full coverage. Seeengineering.md§8.1.
Attribution: Adapted from Rich Hickey's Simple Made Easy (Strange Loop, 2011) and Srid Ratnakumar's implementation. Full talk transcript: matthiasn/talk-transcripts.
Simple (sim-plex, "one fold/braid"): One concern, one role, one concept. Simplicity is objective — count the interleaved concerns. Hickey: "What matters for simplicity is that there is no interleaving, not that there's only one thing."
Easy (adjacens, "nearby"): Familiar, at hand, within our skillset. Easy is relative. Hickey: "If you want everything to be familiar, you will never learn anything new."
Complect (com-plectere, "to braid together"): Interleave independent concerns so they cannot be reasoned about in isolation. Hickey: "Every time I think I pull out a new part of the software I need to comprehend, and it's attached to another thing, I had to pull that other thing into my mind because I can't think about the one without the other."
Compose (com-ponere, "to place together"): Combine independent things side by side, preserving isolation. Hickey: "I'd rather have more things hanging nice, straight down, not twisted together, than just a couple of things tied in a knot."
Work through these layers in order. Every finding must survive self-verification — after completing all layers, run the Self-Verification Checklist (§ below) on your own evaluation to catch wishful justifications and bogus dismissals.
Name the independent concerns the code addresses. Write them out explicitly. If you can't cleanly name distinct concerns, that is itself a finding.
Hickey's "don't complect" has a dual the rest of this workflow doesn't cover: don't fragment what belongs together. When one domain concept is split across multiple fields, state locations, signals, modules, or call sites, and their coherence depends on an unenforced rule, you have the same structural bug as complecting — you just arrived at it from the opposite direction. The fix for complecting is separation. The fix for fragmentation is reunification at whatever layer the one thing naturally lives: one type, one signal, one module, one function, one file.
For every group of related fields, state locations, or entities in the changed code, ask: does the domain model this as one thing?
If yes, is the code representing it as one thing, or has it been shattered into parts whose coherence depends on an unenforced rule? The rule can live anywhere — type shape, code convention, doc comment, "we remember to update both", runtime ordering, a reactive pipeline that rebuilds the unity at read time.
Hickey: "Be particularly careful not to be fooled by code organization. There are tons of libraries that look — oh, look, there's different classes; there's separate classes. They call each other in these nice ways."
For each new abstraction (component, module, signal, type) the code introduces:
Two abstractions serving one user-level concern = accidental concept multiplication, even if each is internally clean. The structural layers below won't catch this — they check within abstractions, not across them.
Concept Multiplication vs. Fragmentation: these are close cousins but distinct bugs. Concept Multiplication is about duplicated wholes (two classes for one domain concept → delete one). Fragmentation (Layer 2) is about split wholes (one domain concept shattered across multiple locations → collapse to one). A single finding can trigger both layers from different angles; that's redundancy, not muddling.
Scan for known structural patterns and any additional patterns from project instructions. The catalog has two halves: complecting (things braided together that should be separate) and fragmentation (things split apart that should be one). Both directions are "interleaved vs. not-interleaved" — Hickey's principle is bidirectional.
Complecting patterns (things-that-should-be-separate braided together)
| Construct | What it complects | Simpler alternative |
|---|---|---|
| Mutable state | Value + time + identity | Immutable values, controlled state containers |
| Objects | State + identity + value + namespace | Plain functions + data + namespaces |
| Methods | Function + state; function + namespace | Free functions, interfaces |
| Inheritance | Types with types | Composition, interfaces, traits |
| Switch/case on type | Who + what | Dynamic dispatch, visitor pattern |
| Mutable variables | Value + time | const/final/let bindings, immutable data |
| Imperative loops | What + how + when | map/filter/reduce, declarative transforms |
| Actors | What + who | Queues + stateless handlers |
| ORM | Object identity + relational model + query | Plain data + declarative queries |
| Conditionals scattered across code | One decision braided across many sites | Rules, declarative policies, lookup tables |
| Callbacks/closures over mutable state | Control flow + state + time | Streams, queues, immutable values |
| Hand-rolled utility when a focused library solves the exact problem | Scope decision with implementation choice | Use the library. Hand-roll only when the library adds surface area you actively don't want. "Zero deps" is an easiness judgment dressed as a simplicity judgment — code you don't own is genuinely simpler than code you do own. |
Fragmentation patterns (things-that-belong-together split apart)
| Construct | What it fragments | Simpler alternative |
|---|---|---|
| Parallel optional/nullable fields with coupled presence | "Thing exists + its shape" into independent slots whose combinations include illegal states | Discriminated union / single container encoding the coupling |
| Per-entity state the domain says must agree across entities | One value into N copies indexed by identity, with an unenforced "all agree" rule | Single source-of-truth at the containing scope |
| Reactive pipeline projecting one value out of a per-entity structure | Sharedness reconstituted at read time | Make the underlying state shared; delete the pipeline |
| Callback-down + value-up across module boundaries | One state location into a cycle across N modules | Lift the state to the layer all consumers share |
| Sum type modeled as parallel optional fields per variant | A discriminator scattered into its projections | Actual sum type with the discriminator as the key |
| Booleans whose combinations encode a state machine | One state into several independent flags | Enum / union naming each reachable state |
| "Convention: update X when Y changes" maintained by memory | A rule into documentation or code review discipline | Structure so the coupling is mechanical, not memorized |
| Duplicated derivations (same value computed in N places) | One computation into N copies | Compute once, read N times |
| Config or data split across files/modules by accident of history | A concept into shards held together by cross-reference | Collapse into the one file or module that owns the concept |
When you find a catalog match in either half, do not dismiss it. Design the concrete alternative first (Layer 7), then evaluate whether the current approach is actually justified. The proof burden is on the current code, not on you to prove it's wrong. Hickey: "what matters are the artifacts not the authoring."
For each file or module touched:
let bindings + reassignments. 3+ in a single scope = scrutinize.For every finding, assess — but severity does not grant dismissal. A low-severity finding is still a finding. Report it. The user decides what to act on, not you.
For every finding — not just "significant" ones — propose a concrete structural alternative. Write out what it would look like, even as pseudocode. This is mandatory.
Never label a finding "essential complexity" without first designing the simplified version. Hickey: "Simplicity is a choice. It's your fault if you don't have a simple system." Assume the existing code came from someone who didn't know better. Design the simpler version. Only after you have it in hand can you evaluate whether the current approach is justified — and if so, explain exactly what makes the simplified version non-viable.
Follow Hickey's design questions:
After completing all layers, audit your own output against this checklist. If any item fires, revise before presenting to the user.
"Out of scope for this PR", "pre-existing issue", "appropriate scope for a bug fix", "orthogonal", and "follow-up refactor" are not simplicity judgments — they are process judgments that let findings evaporate. If you find yourself writing one, either fix the finding in this PR or defer it with a tracked issue. The Actions section enforces this — if you can't fill it in, you're dismissing the finding.
Any finding described as "pre-existing" or "orthogonal" that does not appear in the Actions section with a Defer disposition and an issue reference is a dismissal, not a deferral.
These phrase shapes mean you stopped reasoning one step early. If any appear in your evaluation, re-open the question they're dismissing:
Concerns identified — Name the distinct concerns.
Fragmentation findings — Layer 2 findings. If none, write "no invariants found" explicitly.
Concept multiplication — Layer 3 findings.
Structural pattern matches — Layers 4–5 findings (both complecting and fragmentation halves of the catalog), with line references.
Severity — For each finding: blast radius, change friction, reasoning load.
Simplifications — Concrete alternative for every finding.
Self-verification result — Output of the Self-Verification Checklist on this evaluation, including the premature closure phrase check.
Actions — One entry per finding, formatted for downstream consumption. Every finding from every layer must appear here — including findings labeled "pre-existing", "orthogonal", or "not introduced by this PR". A finding that never reaches this section has been dismissed, not deferred.
Each entry starts with a short bolded finding label (≤8 words) that names what is wrong, then dispositions it as exactly one of:
Example: **viewportDimensions complects current+default roles** — Fix in this PR: delete the signal, replace with per-tile FitAddon measurement.
"No findings" → "No actions." But if findings exist and the actions list is empty, the evaluation is incomplete.
Do NOT include a "What's simple" section. Praise biases toward positive framing and makes findings feel like minor quibbles. Report what you found. The absence of findings is its own praise.
/lowy.npx claudepluginhub nrdxp/predicate --plugin predicateAnalyzes codebases or module namespaces for simplification opportunities like dead code, shallow abstractions, misplaced concerns, unnecessary indirection, and complected state. Produces classified report with recommended actions. For 'simplify this system', 'reduce complexity', or 'find dead code' requests.
Discovers architectural friction — shallow modules, god files, duplication, coupling — and proposes structural refactors with competing interface options and a project-local RFC.
Simplifies code for clarity and maintainability without changing behavior. Useful after features work, during code review, or when refactoring complex logic.