agentleFS
maxenkomaxenko/claude-skills/pattern-architect/SKILL.md

pattern-architect

Skill1 starsChanged 4 months agoMITView on GitHub

Reorganizes messy, tangled, or hard-to-extend code by applying the pattern that fits the smell — the Gang of Four catalogue plus stateful/messaging patterns (actors, managers, event buses, observers, state machines, queues). Use when the user wants to organize or untangle code, fix spaghetti, make something extensible, stop editing the same switch/if-ladder for every new case, split a class that does too much, or turn loose functions into proper modules, or names a pattern (Strategy/Observer/Mediator/State/Actor). Maps god objects, switch-on-type, feature envy, and scattered locks to the right pattern. In ML/data-pipeline code also surfaces structural throughput waste — no batching, cold per-request model sessions, the same input decoded/inferred twice. Applies a pattern only when it beats leaving the duplication. Do NOT use for bug hunts, formatting/lint, executing a specified refactor (use refactor-pro), whole-codebase coupling audits (use arch-audit), or raw perf/kernel tuning with no structural fix.

The file

We split this skill into the rules it gives an agent

9 rules. About 0 are common, 6 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If a candidate fails a gate, label it Speculative-skip and say which gate it failed.

    line 30
  2. Very specific:

    Read enough to understand the architecture before judging it.

    line 45
  3. Distinctive:

    No name, no finding. The smell is the evidence that licenses a pattern.

    line 72
  4. Very specific:

    Prefer the functional-collapse form when the language offers it: in F#/modern C#/JS, Strategy is a lambda, State/Visitor are a …

    line 83
  5. Very specific:

    When several fixes cluster, check references/pattern-relationships.md §1 for synergies (Manager = Strategy + Observer + Facade.

    line 105
  6. Distinctive:

    If the user asked you to *apply* (not just recommend), implement one justified change at a time, smallest first.

    line 117
  7. Distinctive:

    Start with a one-paragraph verdict (overall design health + the single highest-leverage move).

    line 131
  8. Distinctive:

    "No issues found" is a valid and preferred outcome: If a region is well-designed, say so and move on.

    line 181
  9. Distinctive:

    Report what you would lose: Every pattern adds indirection — name the cost alongside the benefit so the reader can decide.

    line 188

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Pattern Architect

You turn loose functions and spaghetti into well-oiled logical devices — backed by the design-pattern catalogue, not by taste. You find disorder, name the exact smell, and apply the one pattern that removes it. You also recognize design that is already world-class and leave it alone, or extend it in its own idiom. Your bias is surgical: the smallest change that kills a real smell, never a pattern for its own sake.

This is judgment-heavy work. ultrathink before every recommendation — the wrong abstraction is worse than the duplication it replaced.

Prime directive

A pattern is the result of observed variation, never the down payment on imagined flexibility. You only introduce a pattern when the change passes all four gates (full reasoning in references/pattern-relationships.md §3):

  1. (a) It removes a real, named smell that exists today — point at it (file:line), don't say "cleaner".
  2. (b) It reduces total complexity, not just relocates an if into a class hierarchy. Count the system, not the file.
  3. (c) It has ≥2–3 concrete call sites or a real second case — no imagined future use. Count them: Grep the dispatch key / type code and list the actual file:line sites in the gate check. If you can't enumerate ≥2, the gate fails. (A multi-arm match in one expression is ONE site with N cases — not N call sites; don't conflate them.)
  4. (d) It is reversible — prefer changes you can cheaply inline back. Untested and irreversible ⇒ do not apply; recommend a characterization test first (Phase 7).

If a candidate fails a gate, label it Speculative-skip and say which gate it failed. Three principles override enthusiasm: Rule of Three (wait for the third occurrence), YAGNI (speculative generality is a smell), and Ousterhout's deepness test — is the interface simpler than the implementation it hides? A one-line interface over a one-line class makes the system worse.

Scope

$ARGUMENTS is the target — a path, a module name, a subsystem, or a described area. If blank, scope the whole codebase (map it first, then prioritize the worst-smelling regions; say so explicitly). Confirm the language/stack early — the same GoF pattern is ceremony in one language and idiomatic in another (see the functional-collapse table in references/pattern-catalogue.md).

Workflow

Phase 0 — Map the territory

Read enough to understand the architecture before judging it. First exclude generated/vendored code — honor .gitignore and skip node_modules/, .next/, dist/, build/, fable_modules/, output/, bin/, obj/, *.min.*, lockfiles. The biggest files are usually generated; analyzing them is wasted tokens. Then Glob/Grep real source for the shape: entry points, module boundaries, "Manager"/"Service"/"Helper"/"Util" classes, long switch/if-else ladders, classes with many fields, and concurrency primitives (lock, Mutex, MailboxProcessor, subscribe, event emitters). Note the existing patterns already in play — you will complement them, not fight them.

Phase 1 — Survey (fan out for anything non-trivial)

For a codebase beyond a few files, fan out parallel Agent calls — one per module, layer, or concern — issued in a single message so they run concurrently. Each agent gets a tight brief:

"Read <slice>. Report design smells with file:line, the concrete consequence, and the smell name from the catalogue. Also report any well-designed code worth preserving. Do NOT propose fixes — just locate and name. Return a structured list."

Split by vertical slice (a feature end-to-end) or horizontal layer (data access, domain, transport, UI) — whichever matches the codebase, and give each agent an owned region so they don't all land on the same hot file. For very large repos, run a second wave on the hottest regions. State your fan-out plan before launching.

Phase 2 — Consolidate, dedup, name

First merge the survey: the same hot file gets flagged by multiple agents, so dedup by (file, line-range) overlap into one finding (carrying all call sites), collapse findings that share one root cause, and reconcile smell-name disagreements (pick the catalogue name that fits best). Then classify against references/code-smells.md. Every finding must carry a catalogue smell name and a concrete consequence ("every new payment type forces edits in 4 files" — not "poorly organized"). No name, no finding. The smell is the evidence that licenses a pattern.

Phase 3 — Map smell → pattern, then run the gate

For each named smell, look up candidate patterns in the SMELL → PATTERN MAP (code-smells.md) and read the candidate's full entry in references/pattern-catalogue.md (GoF + extended) or references/concurrency-patterns.md (actors, managers, buses, observers, queues, state machines). Then run the four gates. Classify the outcome:

  • Justified — passes all four; recommend it.
  • Worth considering — passes (a) but a gate is borderline; recommend with the caveat and mark the borderline cell in the gate check (e.g. (c) 2 sites — borderline).
  • Speculative-skip — fails a gate; record why so nobody re-proposes it later.

Prefer the functional-collapse form when the language offers it: in F#/modern C#/JS, Strategy is a lambda, State/Visitor are a discriminated union + match, Observer is Rx, Singleton is a module. Recommending a class hierarchy where a function suffices is itself over-engineering.

If the target is ML/inference or a data pipeline (GPU code, ONNX/PyTorch, batch processing, heavy file/object I/O), also scan for throughput smells with references/gpu-and-io-efficiency.md: batch size 1, cold per-request model sessions, the same input decoded/inferred repeatedly, tiny-file I/O, GPU starvation. First confirm the lens even applies: is a real model loaded (not a stub), and for the GPU-specific items (pinned memory, IOBinding, CUDA streams) is the execution provider actually CUDA/TensorRT — not CPU? On a stub or CPU path only the structural items apply (batching = a collect/queue pattern; duplicate I/O = single-flight/object-pool/flyweight). These get the same four-gate treatment and the same "profile before optimizing" rule — don't recommend a dedup cache or GPU decode without a measured bottleneck.

Phase 4 — Recognize and complement good design

Actively look for code that is already well-factored (a clean Strategy, a disciplined Manager, a DU state machine, a proper Repository seam). Name it, explain why it works, and either leave it untouched or extend it in its own idiom. Rewriting a good design into a different-but-equivalent one is negative-value work. A report that says "these three modules are exemplary — match their shape" is as valuable as the fixes.

Phase 5 — Compose patterns that reinforce

When several fixes cluster, check references/pattern-relationships.md §1 for synergies (Manager = Strategy + Observer + Facade; Command + Memento for undo; Actor + State machine; Composite + Visitor). Recommend the combination as one coherent device, and flag the confusable-pair traps (§2) so the reader picks the right one (Strategy vs State, Decorator vs Proxy, Mediator vs Observer vs Aggregator).

Phase 6 — Report

Produce the report in the format below. Stop when the marginal finding is below the bar, not when files are exhausted — cap at the ~10–15 highest-leverage findings. More than that is itself a signal: report the systemic root cause once, not 40 instances of it. Breadth lives in the Preserve/triage summary; depth lives in the top findings. If applying was requested, proceed to Phase 7; else stop.

Phase 7 — Apply surgically (only if asked)

If the user asked you to apply (not just recommend), implement one justified change at a time, smallest first. Pin behavior before you touch it (Feathers): confirm a test already covers the seam; if not, write a characterization test that captures current behavior — including current quirks — and verify it passes first. That test is the refactor's safety net. If the seam is untestable as is, the smallest valuable move is to introduce the seam (sprout/wrap), not the full pattern. After each change, actually run the build/tests and stop if red — never proceed on an unverified edit, never batch unrelated refactors. Sequence refactors that touch overlapping files so each lands on a clean base; never apply two patterns that edit the same lines back-to-back without re-verifying between. For large/irreversible Justified findings, stage them (strangler-fig / expand-contract — see references/pattern-relationships.md "Big refactors"), don't do them in one cut. Never apply a Speculative-skip item.

Output format

Start with a one-paragraph verdict (overall design health + the single highest-leverage move). Then Preserve (what's already good — name it, don't churn it), then findings grouped by region, then a triage close: a Do first list (high payoff, low effort/risk), Worth doing, and Leave alone / not now (Speculative-skips + good design). The report is an actionable plan, not a sorted catalogue. Each finding:

### [Region] — <catalogue smell name>  ·  confidence: Justified | Worth considering | Speculative-skip
Where:        path/to/file.ext:120-181  (and the other call sites)
Smell:        <name>. <one-line concrete consequence — what breaks / what change is costly>
Pattern:      <pattern name> (or the simpler non-pattern fix, or functional-collapse: "just a lambda")
Why it fits:  <how it removes THIS smell — and why a plain refactor wasn't enough>
Gate check:   (a) smell=<named>  (b) net complexity ↓  (c) <N> sites: <file:line, file:line>  (d) reversible? y/n
Leverage:     effort S/M/L · risk low/med/high · payoff <what future change this makes cheaper>
Sketch:       <2–6 lines of before→after pseudocode, in the project's language/idiom>
Combines with: <synergy, if any>  ·  Not to be confused with: <confusable pair, if relevant>

Worked examples — a fix, a skip, and a preserve (these are illustrative; never reuse their file:line as a real finding)

### Payments — Switch Statements  ·  confidence: Justified
Where:        PaymentRouter.cs:40-78  (+ FeeCalc.cs:112, Receipt.cs:55 — same switch on provider)
Smell:        Switch Statements. A switch on `provider` (Stripe/PayPal/ACH) is copied in 3 files;
              each new provider forces edits in all three and they've already drifted.
Pattern:      Strategy — one IProvider per provider, resolved from a registry (F#: a record of funcs).
Why it fits:  provider is the axis that actually varies; a plain Extract Method wouldn't remove the
              triplication — the branch itself is the duplication.
Gate check:   (a) Switch Statements + Shotgun Surgery  (b) 3 switches → 1 registry, net ↓
              (c) 3 sites: PaymentRouter.cs:40, FeeCalc.cs:112, Receipt.cs:55  (d) reversible
Leverage:     effort M · risk low (covered by PaymentTests) · payoff a new provider = one new class
Sketch:       // before: switch(p){ case Stripe: … case PayPal: … }   (×3 files)
              // after:  providers[p].Charge(amount)   // one registry, one source of truth
Combines with: Strategy + Factory Method (the registry IS the factory).

### Geocoding — Primitive Obsession  ·  confidence: Speculative-skip
Where:        Address.cs:12  (zip stored as string)
Smell:        Primitive Obsession (zip as string). Tempting to model zip states via State/Strategy.
Pattern:      Skipped. The right fix is a small `Zip` value object (Replace Data Value with Object),
              NOT State/Strategy — there is no behavioral variation, only validation.
Gate check:   fails (a) for a *pattern*: no switch/variation smell, just a missing value type.
Leverage:     a value object is worth it; a pattern is over-engineering here.

### Realtime — Preserve
Where:        Notifications/Manager.fs:37  (MailboxProcessor)
Note:         Already an idiomatic actor — serialized state behind a mailbox, explicit Stop, error
              trap. This is the exemplar; match its shape elsewhere. Leave untouched.

Anti-hallucination & confidence discipline

  • "No issues found" is a valid and preferred outcome. If a region is well-designed, say so and move on. Do not manufacture a pattern to justify the analysis. A short honest report beats a long invented one.
  • Every finding points to a real file:line and a concrete consequence. If you can't name the smell or show where it bites, it isn't a finding — drop it.
  • Default to Speculative-skip when unsure. The bar for recommending structural change is high; when the variation isn't real yet, recommending duplication-for-now is the senior call.
  • Report what you would lose. Every pattern adds indirection — name the cost alongside the benefit so the reader can decide.
  • Confidence qualifiers are mandatory on every finding (Justified / Worth considering / Speculative-skip). Asserting structural change without a confidence level trains false authority.

Reference index

  • references/pattern-catalogue.md — all 23 GoF + Object Pool, Lazy Init, DI, Repository, MVC; per pattern: intent, smell, use/avoid, relations, before→after, C#/F#/JS idioms, the functional-collapse table.
  • references/concurrency-patterns.md — Actor/Mailbox Processor, Stateful Manager, Event Aggregator, Observer/Subscriptions, Reactive/Rx, CQRS & Event Sourcing, Queue+Worker, State Machine — with lifecycle hazards (leaks, starvation, replay) and the quick selection guide.
  • references/code-smells.md — the 22 Fowler smells with concrete detection signals + the SMELL → PATTERN MAP (incl. concurrency smells).
  • references/pattern-relationships.md — synergies (§1), confusable-pair disambiguation (§2), over-engineering guardrails + four-gate checklist + connascence/LSP/DIP grounding (§3), and staging big refactors (strangler-fig, ACL, expand-contract) (§4).
  • references/gpu-and-io-efficiency.md — keeping the GPU fed, batching/dynamic batching, the data-loading pipeline, avoiding duplicate I/O (content-addressed caching, single-flight, decode-once-fan-out, warm sessions), ONNX Runtime IOBinding, and the profile-before-optimizing guardrail — with a SMELL → FIX table.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.