
Click Path Audit
Audit every button and handler through full state transitions when users say controls are broken but unit tests pass.
Overview
Click Path Audit is an agent skill most often used in Ship (also Build, Operate) that traces buttons and touchpoints through state sequences to find mutually canceling handler side effects.
Install
npx skills add https://github.com/affaan-m/everything-claude-code --skill click-path-auditWhat is this skill?
- Maps onClick/onSubmit/onChange handlers through full state change sequences
- Targets shared-store side effects (Zustand/Redux/context) that cancel prior updates
- Documents the composeMode vs selectThread race pattern from a real 54-bug hunt
- Use after major refactors that touch global client state
- Explicitly covers what traditional debugging does not: final UI truth vs button label
- 54 bugs cited in systematic debugging example where this class was missed
Adoption & trust: 3.7k installs on skills.sh; 210k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Users report broken buttons and wrong UI states even though each handler runs without errors and systematic debugging found nothing obvious.
Who is it for?
Solo frontend builders on React/Zustand-style apps after refactors or elusive “button does nothing” bug reports.
Skip if: Pure backend or API-only services with no interactive UI state to trace.
When should I use this skill?
Systematic debugging found no bugs but users report broken buttons, or after any major refactor touching shared state stores.
What do I get? / Deliverables
You get a touchpoint-by-touchpoint audit map that exposes race conditions, undoing side effects, and mismatches between labels and final UI state.
- Per-touchpoint handler trace
- Final-state vs intent mismatch report
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Ship is where broken UX and state races surface before release; this audit belongs on the testing shelf even though it also helps after refactors in Build. It traces interactive touchpoints end-to-end—complementing systematic debugging when reported behavior still fails in the UI.
Where it fits
Reproduce ‘New Email’ doing nothing after QA signed off handler wiring.
Validate navigation and modal flows right after merging a global store refactor.
Investigate intermittent reports that toggles revert when support cannot reproduce crashes.
How it compares
Use as a behavioral checklist on top of systematic debugging—not instead of linting, types, or automated test suites.
Common Questions / FAQ
Who is click-path-audit for?
Indie developers shipping client apps who debug UI state interactions and shared stores rather than only server logs.
When should I use click-path-audit?
Use it in Ship testing when QA passes but UX fails, in Build frontend after store refactors, and in Operate when production users report inconsistent control behavior.
Is click-path-audit safe to install?
It is procedural documentation with no bundled executables described in the excerpt; review the Security Audits panel on this Prism page for the full package.
SKILL.md
READMESKILL.md - Click Path Audit
# /click-path-audit — Behavioural Flow Audit Find bugs that static code reading misses: state interaction side effects, race conditions between sequential calls, and handlers that silently undo each other. ## The Problem This Solves Traditional debugging checks: - Does the function exist? (missing wiring) - Does it crash? (runtime errors) - Does it return the right type? (data flow) But it does NOT check: - **Does the final UI state match what the button label promises?** - **Does function B silently undo what function A just did?** - **Does shared state (Zustand/Redux/context) have side effects that cancel the intended action?** Real example: A "New Email" button called `setComposeMode(true)` then `selectThread(null)`. Both worked individually. But `selectThread` had a side effect resetting `composeMode: false`. The button did nothing. 54 bugs were found by systematic debugging — this one was missed. --- ## How It Works For EVERY interactive touchpoint in the target area: ``` 1. IDENTIFY the handler (onClick, onSubmit, onChange, etc.) 2. TRACE every function call in the handler, IN ORDER 3. For EACH function call: a. What state does it READ? b. What state does it WRITE? c. Does it have SIDE EFFECTS on shared state? d. Does it reset/clear any state as a side effect? 4. CHECK: Does any later call UNDO a state change from an earlier call? 5. CHECK: Is the FINAL state what the user expects from the button label? 6. CHECK: Are there race conditions (async calls that resolve in wrong order)? ``` --- ## Execution Steps ### Step 1: Map State Stores Before auditing any touchpoint, build a side-effect map of every state store action: ``` For each Zustand store / React context in scope: For each action/setter: - What fields does it set? - Does it RESET other fields as a side effect? - Document: actionName → {sets: [...], resets: [...]} ``` This is the critical reference. The "New Email" bug was invisible without knowing that `selectThread` resets `composeMode`. **Output format:** ``` STORE: emailStore setComposeMode(bool) → sets: {composeMode} selectThread(thread|null) → sets: {selectedThread, selectedThreadId, messages, drafts, selectedDraft, summary} RESETS: {composeMode: false, composeData: null, redraftOpen: false} setDraftGenerating(bool) → sets: {draftGenerating} ... DANGEROUS RESETS (actions that clear state they don't own): selectThread → resets composeMode (owned by setComposeMode) reset → resets everything ``` ### Step 2: Audit Each Touchpoint For each button/toggle/form submit in the target area: ``` TOUCHPOINT: [Button label] in [Component:line] HANDLER: onClick → { call 1: functionA() → sets {X: true} call 2: functionB() → sets {Y: null} RESETS {X: false} ← CONFLICT } EXPECTED: User sees [description of what button label promises] ACTUAL: X is false because functionB reset it VERDICT: BUG — [description] ``` **Check each of these bug patterns:** #### Pattern 1: Sequential Undo ``` handler() { setState_A(true) // sets X = true setState_B(null) // side effect: resets X = false } // Result: X is false. First call was pointless. ``` #### Pattern 2: Async Race ``` handler() { fetchA().then(() => setState({ loading: false })) fetchB().then(() => setState({ loading: true })) } // Result: final loading state depends on which resolves first ``` #### Pattern 3: Stale Closure ``` const [count, setCount] = useState(0) const handler = useCallback(() => { setCount(count + 1) // captures stale count setCount(count + 1) /