Find design smells in a breadboard and fix them. Use after breadboarding to validate affordance boundaries, naming, and wiring correctness.
Find design smells in a breadboard and fix them. Works on existing breadboards built with the /breadboarding skill.
Take a user story from the requirements or frame. Trace it through the breadboard wiring. Ask: does the path tell a coherent story that produces the expected effect?
Example: "User says 'add Tokyo after Detroit' → Tokyo appears after Detroit in the table, and persists across restarts."
Trace: U4 (input) → N1 → N2 (LLM) → N3 (dispatch) → N4 (handle) → ... → S1 (locales updated) → N12 (persist) → S4 (config written).
At each link, ask: does this step logically lead to the next? Does the wiring make sense as a story about how the effect happens?
| Smell | What you notice |
|---|---|
| Incoherent wiring |
| A node writes to S1 AND triggers the thing that writes to S1 — redundant or contradictory |
| Missing path | The user story requires an effect, but no wiring path produces it |
| Diagram-only nodes | Nodes in the diagram that aren't in the affordance tables — decoration, not real affordances |
| Naming resistance | You can't name an affordance with one idiomatic verb (see Naming Test below) |
| Stale affordances | The breadboard shows something that no longer exists in the code |
| Wrong causality | The wiring shows A calls B, but the code shows C calls B |
| Implementation mismatch | The code has logic paths, functions, or call chains that aren't represented in the breadboard |
The first three are visible from the breadboard and requirements alone. The last four require comparing to the implementation — read the actual code and check each affordance: does it exist? Does the wiring match what the code actually calls and returns? Is anything missing?
The primary tool for finding and fixing affordance boundary problems.
For each affordance:
| Signal | Meaning |
|---|---|
| One verb covers all code paths | Boundary is correct |
| Need "or" to connect two verbs | Likely two affordances bundled together |
| Name doesn't feel idiomatic | Boundary is wrong |
| Name matches a downstream effect, not this step | You're naming the chain, not the step |
Name what THIS step does, not the downstream cascade.
Chain-level (wrong): An orchestrator that calls validate, find, extract, and insert is named add_locale — but it doesn't add anything itself. Adding is the chain's effect.
Step-level (right): The orchestrator's own effect is handling/dispatching → handle_place_locale. The adding happens downstream.
How to check:
If what's left is just sequencing and branching, it's a handler. Name it as such.
Names should reflect what the affordance affords from the caller's perspective — the effect the caller achieves by using it.
| Perspective | Question | Example |
|---|---|---|
| Caller | "What can I achieve by calling this?" | N3 calls N4 → "handle place_locale tool call" |
| Step | "What does this function do, not its callees?" | N4 itself → "dispatch to validate, resolve, insert" |
| Effect | "What changes in the system after this runs?" | N15 → "locale is extracted from its position" |
A tool exposed to an external caller (like an LLM) should be named for the effect the caller wants: place_locale — the caller wants to place a locale.
The internal handler that processes that tool call should be named for its own role: handle_place_locale — it handles the dispatch, delegating work to sub-steps.
A function resolve_locale either pops an existing locale from a list OR creates a new dict:
extract_locale (pop) and create_locale (new)The inability to find one idiomatic verb was the signal that this was two distinct operations forced into one function.
When the naming test reveals a bundled affordance:
Never split only in the diagram (e.g., adding unnamed sub-nodes in a subgraph). If it's not a named function in the code and a row in the table, it's not a real affordance.
When the causality is wrong (A → B in the breadboard but C → B in the code):
After any changes: