AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Empathy Audit

skill-carlkibler-agent-skills-empathy-audit · by carlkibler

Review code through user, machine, developer, and support lenses.

— No reviews yet
0 installs
2 views
0.0% view→install

Install

$ agentstack add skill-carlkibler-agent-skills-empathy-audit

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-carlkibler-agent-skills-empathy-audit)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Empathy Audit? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Empathy Audit

A structured review that asks: who does this code serve, and how does it treat them?

Empathy is not the soft part of review — it's a different search strategy. Asking "who suffers when this code actually runs?" routinely surfaces hard defects a correctness pass slides right past: the dictionary that grows unbounded and leaks memory for months, the silent continue that drops a user's data with no log line, the 401 handler that retries forever and cooks the battery, the O(n²) loop nobody noticed because the test fixture had three rows. In practice these lenses have turned up real performance wins and logic errors hiding behind green tests and clean diffs — the kind of findings that materially improve the software, not just its manners.

Technical reviews catch bugs in isolation. Empathy audits catch the things that make users uninstall, machines overheat, developers quit, and support people burn out — and the things worth celebrating: the thoughtful defaults, the generous error messages, the code that's a pleasure to read.

When to Use

  • Before a launch, beta, or public release
  • After completing a major feature or refactor
  • When a product "works" but something feels off
  • When support tickets are accumulating but tests are green
  • Periodically on long-running products (quarterly health check)

When NOT to Use

  • Tiny bug fixes or single-line changes
  • Pure infrastructure/CI changes with no user surface
  • Early prototypes where the design is still fluid

Phase 1: Scope and Context

Before reviewing, establish the review surface:

  1. Read project docs — CLAUDE.md, README, architecture docs
  2. Identify the audience — who uses this? what do they care about?
  3. Map the hot paths — what code runs most often? what touches user files/data?
  4. Size the review — pick the scope:

| Scope | What to review | Time | |---|---|---| | Focused | One feature or subsystem (3-8 files) | Quick | | Module | A full module/layer (10-20 files) | Medium | | Full | Entire codebase | Use multi-agent fan-out |

For a full audit, use the multi-agent approach in Phase 3. For focused/module, a single pass through all four lenses is fine.

The Finding Format

Every finding — across all four lenses — follows the same chain: situation → consequence → action. This is what makes an empathy audit actionable instead of decorative.

### [Short title]
- **Lens:** User / Machine / Developer / Support
- **Type:** POSITIVE or NEGATIVE
- **Situation:** [A concrete scenario. Who is involved, what are they doing, what happens. 2-3 sentences written as a story, not a code description.]
- **Consequence:** [What the person or machine experiences as a result. Be emotionally precise for people — not "frustrated" but "confused and wondering if the app is broken." Be physically precise for machines — not "uses memory" but "grows by ~1KB per failure, never pruned, reaching tens of MB after months of uptime."]
- **Evidence:** [file:function or file:line — the specific code responsible]
- **Action:** [A specific, implementable change. Must be concrete enough that a developer could start working on it without asking a follow-up question. For positives: what to preserve and why.]

What makes a good finding vs. a bad one

Bad (vague symptom → vague fix): > "Silent failures could confuse users. Consider adding better error handling."

This is useless. What silent failure? Which user? What error handling?

Good (concrete situation → clear consequence → specific action): > Situation: A user changes their macOS screenshot format to HEIC in System Settings. They take a screenshot. ScreenSage's watcher sees the new file, but ImageProcessor.isSupportedImageFormat rejects it because only PNG/JPG are supported. The file is silently skipped — no notification, no history entry, no log visible to the user. > > Consequence: The user waits, nothing happens, they assume the app is broken. They may try restarting, checking settings, or uninstalling — never realizing the format is the issue. This is the #1 "why doesn't it work?" support email waiting to happen. > > Evidence: ImageProcessor.swift:106 — isSupportedImageFormat only accepts ["png", "jpg", "jpeg"] > > Action: When a watched folder contains image files in unsupported formats, show a one-time notification: "ScreenSage saw a .heic screenshot but can only rename PNG and JPG files right now. You can change your screenshot format in System Settings > Screenshots." Log it to history as skipped with the reason.

Notice:

  • The situation tells a story you can picture
  • The consequence explains why it matters, not just that it's bad
  • The evidence points to the exact code
  • The action is specific enough to implement without further questions

Phase 2: The Four Lenses

Each lens has a distinct perspective and examines different code. They are designed to find different things — resist the urge to merge them. Each lens prompt below is what you hand to an agent in Phase 3.

Lens 1: User Empathy

Perspective: You are the person who installed this because it promised to solve a problem. You don't know or care how it works internally.

What to examine:

  • Error messages — do they explain why and what to do next?
  • Silent failures — does the app ever fail without telling the user?
  • Defaults — safe for a first-time user, or optimized for the developer's setup?
  • Waiting — are there "dead air" moments with no progress indicator?
  • Destructive actions — do file mutations have clear confirmation and undo?
  • Notifications — helpful or spammy? Present when critical or absent?
  • Privacy — does the user know what data leaves their machine?
  • Cognitive load — does the user need to understand internals to use it?

What this lens catches that others miss:

  • The moment a user decides "this app is broken" (even when it isn't)
  • The gap between what the product promises and what the experience delivers
  • Settings that feel reasonable to the builder but reckless to the user
  • Features that work but feel creepy, sloppy, or untrustworthy

Example positive finding:

### Error messages guide users to fix the problem themselves
- Lens: User Empathy
- Type: POSITIVE
- Situation: A user's OpenRouter API key expires. The next screenshot triggers a 401. The app pauses automation and shows the menu bar status: "AI setup needed" with a primary action "Fix AI settings" that opens Preferences directly to the API key field.
- Consequence: The user understands what happened and can fix it in under 30 seconds without contacting support. They feel competent, not blamed.
- Evidence: SharedTypes.swift — AIError.userFacingMessage returns specific, actionable strings for each error type. AppWatchState.primaryActionTitle provides a one-click path to resolution.
- Action: Preserve this pattern. When adding new error types, always include (1) what went wrong in plain language, (2) what the user should do, and (3) a direct link to the fix.

Example negative finding:

### Burst pause during presentations leaves user stranded
- Lens: User Empathy
- Type: NEGATIVE
- Situation: A developer is giving a live demo, rapidly taking screenshots to document steps. After the 20th screenshot in 10 seconds, ScreenSage silently pauses all processing. A notification says "Large batch paused" but the user is focused on their demo and misses it.
- Consequence: The user finishes their demo, expects 30 renamed screenshots, and finds them all still named "Screenshot 2026-...". They feel betrayed — the app stopped working at the exact moment they needed it most, without clearly telling them.
- Evidence: ProcessingQueue.swift:438 — burst detection at 20 files / 10 seconds triggers `pauseByUser()`.
- Action: Instead of silently pausing, show a persistent menu bar badge or change the status icon. Consider raising the threshold to 50, or making it configurable. At minimum, the resume should be one click from the menu bar, not buried.

Lens 2: Machine Empathy

Perspective: You are the computer. You have finite battery, CPU, memory, disk, and network. Every cycle this app burns is a cycle stolen from something else.

What to examine:

  • Polling vs. events — timers that could be callbacks, notifications, or FSEvents
  • Wake frequency — how often does the app wake the CPU when nothing is happening?
  • Unbounded growth — collections, caches, or dictionaries that accumulate without pruning
  • Network discipline — redundant calls, retries without backoff, downloading more than needed
  • Disk I/O — constant small writes, temp files not cleaned up
  • Image/media — full-resolution assets when thumbnails would do
  • Background behavior — work happening when the user isn't looking
  • File descriptors / connections — properly closed? Reused?

What this lens catches that others miss:

  • The "fan spin" — technically correct code that makes the laptop hot
  • The "battery thief" — background activity that drains power without user benefit
  • The "slow leak" — growth that only matters after weeks of uptime in a menu bar app
  • Timers that could be event-driven

Example negative finding:

### Failure tracker grows unbounded over app lifetime
- Lens: Machine Empathy
- Type: NEGATIVE
- Situation: ScreenSage runs in the menu bar 24/7. Each time a file fails processing, its path is added to `repeatedFailureCounts`. This dictionary is never pruned — not on success, not on timer, not on app state change.
- Consequence: After months of uptime processing hundreds of files, this dictionary accumulates hundreds of entries consuming memory that serves no purpose. For a menu bar app that users expect to be invisible, any unbounded growth is a violation of trust with the machine.
- Evidence: ProcessingQueue.swift:130 — `private var repeatedFailureCounts: [String: Int] = [:]` — no pruning logic exists anywhere in the file.
- Action: Add a prune step to the existing `checkForStuckItems()` 60-second timer: remove entries older than 1 hour or with counts below the action threshold. Alternatively, switch to a fixed-size LRU.

Example positive finding:

### Directory watching uses FSEvents, not polling
- Lens: Machine Empathy
- Type: POSITIVE
- Situation: The DirectoryWatcher uses DispatchSource file system events to detect new screenshots, waking only when the OS signals a change.
- Consequence: Zero CPU cost when no screenshots are being taken. The Mac can sleep peacefully. This is exactly the right pattern for a menu bar app.
- Evidence: DirectoryWatcher.swift:119 — `DispatchSource.makeFileSystemObjectSource` with appropriate event masks.
- Action: Preserve this. If adding new watched resources (e.g., watching for settings changes), use the same event-driven pattern rather than introducing timers.

Lens 3: Developer Empathy

Perspective: You are the next person — human or AI — who opens this codebase at 2 AM to fix a production bug. You've never seen it before.

What to examine:

  • God objects — files or classes doing too many things
  • Naming — do names tell you what things do, or do you need to read the implementation?
  • Coupling — how many singletons must you understand to change one thing?
  • Consistency — does the codebase follow its own conventions?
  • Hidden side effects — does calling a function change state you wouldn't expect?
  • Error tracing — can you follow a failure from log to responsible code in 60 seconds?
  • Test clarity — do tests explain behavior, or are they puzzles?
  • Architecture legibility — can you understand the system from the directory structure?

What this lens catches that others miss:

  • The "fear of breaking" — code that works but nobody dares touch
  • The "archaeology problem" — understanding requires git blame, not reading code
  • The "clever trap" — impressive code that creates maintenance debt
  • The "welcome mat" — code that's genuinely pleasant to work in

Example negative finding:

### ProcessingQueue is a 1400-line god object
- Lens: Developer Empathy
- Type: NEGATIVE
- Situation: A new developer needs to fix a bug where AI descriptions sometimes contain markdown formatting. They open ProcessingQueue.swift looking for the sanitization logic. The file is 1400 lines containing file I/O, AI orchestration, state management, response sanitization, notification dispatch, circuit breaking, burst detection, and queue diagnostics.
- Consequence: The developer spends 20 minutes finding `sanitizeDescription` buried at line 1058 because there's no clear boundary between concerns. They're afraid to change it because every function touches shared mutable state. A 10-minute fix takes an hour.
- Evidence: ProcessingQueue.swift — 1390 lines, 5+ singleton dependencies, handles 7+ distinct responsibilities.
- Action: Extract `sanitizeDescription` and response validation into a standalone `CaptionSanitizer` struct with its own tests. This is the lowest-risk first step — it's a pure function with no state dependencies. Future extractions: circuit breaker logic, burst detection, queue diagnostics.

Example positive finding:

### Error taxonomy makes failure handling predictable
- Lens: Developer Empathy
- Type: POSITIVE
- Situation: A developer needs to add handling for a new API error code. They look at AIError and immediately see the pattern: each case has `isTerminal`, `isRetryable`, `shouldStopAutomation`, `userFacingMessage`, and `errorCode`. Adding a new case is obvious — follow the existing pattern.
- Consequence: The developer adds the new case in 5 minutes with confidence. The enum's behavioral properties mean the rest of the codebase handles it correctly without changes.
- Evidence: SharedTypes.swift:109-208 — AIError enum with computed behavioral properties.
- Action: Preserve this pattern. It's a genuine pleasure to extend. When adding new error surfaces (e.g., StoreKit errors), model them the same way.

Lens 4: Support Empathy

Perspective: You answer the email when a user says "it's broken." Every silent failure is a mystery you must solve with a vague user report and whatever breadcrumbs the app left behind.

What to examine:

  • Logging — if this fails, will logs show what input caused it and what state the system was in?
  • Error specificity — does the user see "Something went wrong" or a message that helps them self-fix?
  • Diagnostics — can you export a redacted bundle? Is there a "system check"?
  • Silent drops — code paths where work is skipped with no log, no notification, no history entry
  • Self-service — can the user fix common problems without contacting you?
  • Feedback channels — is it easy to report? Does the report include useful context?
  • State visibility — can you tell what the app is currently doing?

What this lens catches that others miss:

  • The "20-email thread" — a problem that takes excessive back-and-forth to diagnose
  • The "ghost failure" — something went wrong but there's zero evidence
  • The "works on my machine" — failures only in user environments you never tested
  • The "brilliant diagnostics" — systems that make support a pleasure

Example negative finding:

### Skipped files leave no trace for support to investigate
- Lens: Support Empathy
- Type: NEGATIVE
- Situation: A user reports "ScreenSage stopped working." Support asks them to send a diagnostics bundle. The bundle shows healthy status, valid API key, watched folder accessible. Everything looks fine. But the user's screenshots are all .heic files, which ScreenSage silently skips — no log line, no history entry, no notification.
- Consequence: Support cannot determine the cause from the diagnostics bundle alone. They ask the user to take a test screenshot, the user takes another .heic, nothing happens, support is stumped. The resolution requires 4+ emails and eventually asking "what format are your screenshots?" — something the diagnostics should

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [carlkibler](https://github.com/carlkibler)
- **Source:** [carlkibler/agent-skills](https://github.com/carlkibler/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.