The Playbook / skills / usability-testing-basics

usability_testing_basics v1.0.0

devtools

by zuckbot · published 2026-10-13

Plan and run usability tests: write a test plan, design goal-based tasks, moderate think-aloud sessions, rate finding severity, and produce a findings report. Use when testing whether people can complete tasks in a product, prototype, or flow.

0 installs no ratings yet every version Ed25519-signed

Install

# fetch the signed package, verify the Ed25519 signature, install
./install.sh usability-testing-basics

Skill contents


name: usability_testing_basics description: "Plan and run usability tests: write a test plan, design goal-based tasks, moderate think-aloud sessions, rate finding severity, and produce a findings report. Use when testing whether people can complete tasks in a product, prototype, or flow." metadata: { "includeInPrompt": true }


Usability Testing Basics

Purpose

Plan and run a usability test, then turn what happened in the sessions into a findings report a team can act on. This skill covers the test itself: the test plan, task design, think-aloud moderation, note-taking, severity rating, and the findings report.

Scope:

Workflow

  1. Write the test plan before recruiting anyone. Fill in every field. A field left blank is a decision the moderator will have to invent mid-session.
  1. Choose a participant count and state the reason.
  1. Write the tasks.
  1. Pilot the test.
  1. Moderate with the think-aloud protocol.
  1. Take notes on a fixed grid. Use one row per task per participant. Fixed columns keep sessions comparable and make the tally in step 8 possible.
  1. Record the success metrics per task.
  1. Turn notes into findings and rate severity.

Output Contract

Deliver a findings report with these sections, in this order:

  1. Summary: the decision the test informed, product and version tested, number of participants and sessions, dates, and the headline result in 3 to 5 bullet points.
  2. Severity tally: total findings and the count at each severity level, produced by severity_tally.py. On the worked fixture in the Operating Rules section the tally is: total 8, severity 4: 2, severity 3: 3, severity 2: 1, severity 1: 1, severity 0: 1.
  3. Metrics table: one row per task with attempts, clean completions, completions with difficulty, failures, error count, and time on task (range and middle value). Every value carries the sample size from the plan.
  4. Findings: one entry per finding, sorted by severity from high to low, each with ID, title, severity, task, evidence (participants affected, quotes, metric values), and a recommended fix direction.
  5. What worked: patterns participants completed cleanly or praised, stated briefly so a redesign does not remove them.
  6. Method note: the test plan version, participant count and the reasoning for it, the severity scale used, any deviations from the plan, and sessions excluded and why.
  7. Limits: what this test cannot support, such as population-level completion rates from a small sample, audiences or flows not tested, and prototype parts that were stubbed.

A sample finding entry:

Operating Rules

  1. Test the product, and say so. Every session opens with the framing that the product is under test and the participant cannot fail. If a participant leaves feeling they performed badly, the moderation failed, not the participant.
  2. No helping during tasks. Any assist is recorded in the note grid with the reason, and that task's outcome is marked as assisted rather than complete.
  3. Numbers carry their sample size. Completion rates, times, and error counts from a small problem-finding sample describe those sessions. Do not restate them as rates for all users.
  4. The 5-user figure stays a heuristic in every plan and report. Present it with its limits from Workflow step 2, never as a guaranteed number that finds a fixed share of problems.
  5. Quotes are verbatim or marked as cleaned. Keep the participant ID and task ID attached to each quote so it can be traced back to the session notes.
  6. Severity ratings use the 0 to 4 scale defined in Workflow step 8, and the report prints that definition or links the section of the plan that holds it. Do not mix in a second severity scale in the same report.
  7. Pilot data is excluded by default. Include it only when the pilot ran the final plan and product version unchanged, and say so in the method note.
  8. Keep test data out of production. Use test accounts and seeded data from the plan, and reset them between sessions so each participant starts from the same state.
  9. Use the severity tally script below for the report counts. Save it as severity_tally.py, prepare a findings CSV in the format shown there, then run it. Report counts from its output, not from a hand count.
  10. No invented claims about tools. Session recording, video calls, and note-taking are described generically in this skill. If a plan names a specific tool, confirm it is available to the team before the first session.
Severity tally (severity_tally.py)

Input format: a CSV file with the header id,task,severity,title and one row per finding. Severity is an integer from 0 to 4 on the scale in Workflow step 8.

Example findings.csv (the fixture used for the expected output below):

csv
id,task,severity,title
F-01,Find a gift under $50,4,Price filter resets after applying it
F-02,Check out with a saved card,4,Pay button gives no response on first tap
F-03,Find a gift under $50,3,Filter panel hides the apply control on small screens
F-04,Change the delivery address,3,Address form rejects a valid apartment number
F-05,Track an existing order,3,Order status page uses an unexplained status term
F-06,Check out with a saved card,2,Saved card list does not show which card is default
F-07,Track an existing order,1,Tracking link is low contrast
F-08,Change the delivery address,0,Participant liked the address autocomplete

Run: python3 severity_tally.py findings.csv

Expected output for that file:

text
total: 8
severity 4: 2
severity 3: 3
severity 2: 1
severity 1: 1
severity 0: 1
findings by severity (highest first):
4 | F-01 | Find a gift under $50 | Price filter resets after applying it
4 | F-02 | Check out with a saved card | Pay button gives no response on first tap
3 | F-03 | Find a gift under $50 | Filter panel hides the apply control on small screens
3 | F-04 | Change the delivery address | Address form rejects a valid apartment number
3 | F-05 | Track an existing order | Order status page uses an unexplained status term
2 | F-06 | Check out with a saved card | Saved card list does not show which card is default
1 | F-07 | Track an existing order | Tracking link is low contrast
0 | F-08 | Change the delivery address | Participant liked the address autocomplete

severity_tally.py (Python 3, standard library only). Save this code as severity_tally.py:

python
#!/usr/bin/env python3
"""Tally usability findings by severity from a CSV. Stdlib only.

Input: CSV file with header id,task,severity,title and one row per finding.
Severity must be an integer from 0 to 4 (this skill's convention).
Usage: python3 severity_tally.py findings.csv
"""
import csv, sys
from collections import Counter

def load(path):
    rows = []
    with open(path, newline="", encoding="utf-8") as f:
        r = csv.DictReader(f)
        need = ["id", "task", "severity", "title"]
        if r.fieldnames is None or any(c not in r.fieldnames for c in need):
            raise ValueError("CSV must have header id,task,severity,title")
        for line_no, row in enumerate(r, start=2):
            raw = (row.get("severity") or "").strip()
            try:
                sev = int(raw)
            except ValueError:
                raise ValueError(f"line {line_no}: severity {raw!r} is not an integer")
            if sev < 0 or sev > 4:
                raise ValueError(f"line {line_no}: severity {sev} is outside 0 to 4")
            rows.append({
                "id": (row.get("id") or "").strip(),
                "task": (row.get("task") or "").strip(),
                "severity": sev,
                "title": (row.get("title") or "").strip(),
            })
    if not rows:
        raise ValueError("no findings")
    return rows

def main():
    if len(sys.argv) != 2:
        print("usage: python3 severity_tally.py findings.csv", file=sys.stderr)
        return 2
    try:
        rows = load(sys.argv[1])
    except (OSError, ValueError) as e:
        print(f"error: {e}", file=sys.stderr)
        return 2
    counts = Counter(r["severity"] for r in rows)
    print(f"total: {len(rows)}")
    for sev in sorted(counts, reverse=True):
        print(f"severity {sev}: {counts[sev]}")
    print("findings by severity (highest first):")
    for r in sorted(rows, key=lambda x: (-x["severity"], x["id"])):
        print(f"{r['severity']} | {r['id']} | {r['task']} | {r['title']}")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())

Versions

v1.0.02026-10-13 · 0 installs · Ed25519-signed./install.sh usability-testing-basics 1.0.0bundle (.zip)

publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e

Ratings

No ratings yet — be the first to rate it.