Back
Standalone v1.2.0 Star on GitHub

feedback-synthesizer

Turns raw scores and comments into one connected finding, instead of two reports that happen to share a doc.

Turns raw survey results (scores + open-text comments) into a scored, statistically-tested, thematically-coded synthesis — and connects the two. Computes instrument-correct scores (SUS formula, NPS calc, SUPR-Q percentile), real confidence intervals, and significance testing across waves or segments. Codes qualitative comments into themes (emergent per study, converging toward a fixed taxonomy per product over time) and cross-references low scorers against recurring themes. Owns severity/priority scoring. Can run standalone on any pile of feedback (reviews, support tickets, open-ended comments) — doesn't require a survey-architect-built survey as input. Trigger when someone pastes raw survey data, a CSV of responses, or a pile of qualitative feedback and wants it turned into findings. Entry point 2 of the research loop (see research-loop). Bundles scripts/scoring.py for deterministic instrument math (run it, don't calculate by hand) and evals/ for regression testing after any edit — run evals/test_scoring.py after touching scoring.py, and check evals/qualitative_cases.md after touching this file's prose.

  • Survey results or a pile of feedback

    Scores plus open-text comments from a survey-architect study, or standalone raw data: a CSV, app store reviews, support tickets, anything with opinions attached to it.

  • A prior benchmark, if one exists

    The product's theme taxonomy and score history, so this wave's themes converge with the last one instead of resetting from zero.

  • A scored, tested, and themed synthesis

    The instrument-correct score with its confidence interval and significance test, plus themes coded by judgment and ranked Critical, Notable, or Minor.

  • Qual-quant cross-references

    Every notable score change connected to the theme that explains it, or reported as unexplained when no theme does, instead of a score and a comment summary sitting side by side unconnected.

Load the feedback-synthesizer skill. Here's the data: [paste survey results, a CSV, or raw feedback].

Skills that call into or pair with this one. Click a node to open it.

Get this skill

Download the SKILL.md file and install it in Claude or Cursor.

  1. Download the SKILL.md file using the button above.
  2. Open claude.ai and go to Settings.
  3. Select Skills from the sidebar.
  4. Click Add skill and upload the SKILL.md file.
  5. Give the skill a name and save. Claude loads it automatically when relevant.

Skills installed in Claude persist across conversations. Claude reads the skill when the trigger conditions match your message.

Feedback Synthesizer

Takes raw numbers and raw comments and produces one connected picture, not two separate reports that happen to sit in the same doc.

Most teams either report a score or summarize comments. This skill’s whole job is to make the two inform each other — a score drop with no thematic explanation isn’t a finding yet.


Step 0: What are you looking at?

Identify the input type before doing anything:

  • From survey-architect (a /research/<study-name>/01-survey-spec.json exists) → read it for instrument, scoring formula, and expected n. Use it, don’t re-derive.
  • Standalone raw data (pasted CSV, app store reviews, support ticket export, no spec file) → identify the instrument from the data itself if it’s a known format (SUS items, an 0–10 NPS column, etc.), or treat it as unstructured qualitative feedback if there’s no scale attached. Say explicitly which case you’re in — don’t silently assume a scale that isn’t there.

Step 1: Quantitative scoring

Run scripts/scoring.py — do not compute these by hand. Every instrument formula, the confidence interval, and the significance tests are exact, deterministic calculations with no judgment involved, which is exactly the kind of task that should never depend on an LLM doing arithmetic in its head. Call the matching function for the instrument in use:

InstrumentFunction
SUSsus_score(responses)
UMUXumux_score(responses)
UMUX-Liteumux_lite_score(item1, item2)
NPSnps_score(responses)
CSATcsat_top_two_box(responses) or csat_mean(responses) — state which
SEQ / CESseq_score(responses) / ces_score(responses)
SUPR-Qsupr_q_score(five_point_items, nps_item_0_to_10)

If the instrument doesn’t map to one of these (a custom item set), say so explicitly rather than forcing it through the nearest function.

Confidence intervals and significance testing

Also not optional, also run through the script — call confidence_interval(scores) and, when comparing against a benchmark or prior wave, significance_vs_benchmark(scores, benchmark) or compare_waves(scores_a, scores_b). Never report a headline score without its CI, and never call a difference “significant” without having actually run the test.

State the result in plain language alongside the numbers (“the 6-point drop is outside the margin of error — this is a real change” vs. “the 3-point drop is within the CI — could be noise”).

If scripts/scoring.py isn’t available in the execution environment for some reason, say so explicitly before falling back to manual calculation — don’t silently compute by hand and present it as if the script ran.

Small-sample handling

If n falls below the sample-size floor survey-architect specified (or below the general rule-of-thumb minimums if there’s no spec file):

  • Compute the score anyway. Never refuse.
  • Widen the CI accordingly and say so explicitly.
  • Flag it loudly at the top of the synthesis output, not buried in a footnote: ⚠️ LOW CONFIDENCE — n=<X>, below recommended minimum of <Y>. Treat this score as directional, not conclusive.
  • If the gap is large enough that the score is essentially unusable for the stated decision (see survey-architect’s learning goal), kick back to survey-architect with a note to extend collection — this is the loop-back condition, not a silent proceed.

Step 2: Qualitative coding

  • New product or first study: code comments with an emergent/open approach — let themes surface from the data, don’t force them into a predefined bucket.
  • Product with prior studies: check /research/_benchmarks/<product>.md for the existing theme taxonomy. Reuse those theme labels where the data fits; only add a new theme label if genuinely nothing existing captures it. This is how the taxonomy converges over time instead of resetting every study.
  • For each theme, report: frequency (n and %), representative paraphrased examples (never verbatim quotes — see copyright note below), and whether it skews toward low or high scorers.

Never quote respondents verbatim in synthesis output — paraphrase every comment. This matters for two reasons: respondent privacy in smaller samples, and because verbatim quotes get copy-pasted into decks and lose the aggregation context.


Step 3: Cross-reference qual and quant

This is the step most synthesis work skips. For every notable score change (a wave-over-wave drop, a below-benchmark score, a segment that scores lower than others):

  1. Pull the subset of open-text comments from respondents in that score band.
  2. Check whether a theme is disproportionately represented in that subset vs. the overall theme distribution.
  3. If yes, state the connection explicitly: “SUS dropped 8 points this wave (95% CI: −11 to −5, statistically significant vs. wave 2). 60% of respondents scoring below 60 mentioned the new checkout step, vs. 12% of respondents scoring above 80.”
  4. If no clear theme explains a score change, say that too — an unexplained score movement is itself a finding, not a gap to paper over.

Step 4: Severity and priority scoring

This skill owns severity/priority — not the reporter. Once you have the raw counts (how many respondents in each score band mentioned the theme), run scoring.severity_tier(...) rather than eyeballing the percentages — this is the exact calculation that was wrong in v1.0.0 (diluted by whole-sample frequency instead of within-band frequency), and it’s now a tested function specifically so that bug can’t quietly reappear in prose form. Getting the raw counts right — which respondents go in which band, whether a comment belongs to the theme — is still your judgment call; the arithmetic on top of those counts is not.

Score each theme on two axes:

  • Frequency: what % of respondents in the affected score band raised it — not what % of the whole sample raised it. A theme mentioned by 3 of 4 low scorers is high-frequency even if it’s only 3 of 14 respondents overall. Measuring frequency against the full sample dilutes a concentrated signal and can demote a real driver of low scores to a lower tier than it deserves. Also report the plain overall % for context, but don’t let it drive the tier.
  • Impact: does it correlate with low scores / high effort / low completion — i.e., is the theme’s presence skewed toward one score band vs. another, per the cross-reference in Step 3

Combine into a simple priority tier (not a false-precision numeric score):

  • Critical — high frequency within the affected band (roughly ≥50% of respondents in that band) and a clear skew vs. the unaffected band (that band’s rate is meaningfully higher than the rest of the sample’s rate for the same theme)
  • Notable — meets one of the two Critical conditions, not both (e.g., mentioned by a majority of low scorers but also shows up somewhat among high scorers, or a clear skew but only a small minority of the affected band mentions it)
  • Minor — low frequency within the affected band and no meaningful skew vs. the rest of the sample

State the reasoning for each tier assignment explicitly, including both the within-band and overall frequency numbers — don’t just output the label, and don’t let a low overall frequency read as an argument against a tier the within-band numbers support.

Coding themes from judgment, not keyword matching. A comment can belong to a theme without using any of that theme’s obvious keywords — e.g., “had to re-enter my card info twice” belongs in a payment/checkout friction theme even though it never says “payment.” Read each comment for what it’s actually describing, not for whether it contains a specific word. A literal string search will systematically undercount themes and produce frequency numbers that look lower than reality.


Step 5: Output

Write /research/<study-name>/02-synthesis.md (or .json for the machine-readable version consumed by research-reporter) containing: scores + CIs + significance results, theme list with frequency/severity, qual-quant cross-references, and the low-confidence flag if triggered.

Append the wave’s headline score(s) to /research/_benchmarks/<product>.md so the next study’s comparison has something to compare against. Include date, instrument, score, CI, and n.


What this skill never does

  • Reports a score without its confidence interval
  • Calls a numeric difference “significant” without running the actual test
  • Refuses to score a small sample — flags it loudly and proceeds instead
  • Silently proceeds when the sample is so small the stated learning goal can’t be answered — kicks back to survey-architect instead
  • Quotes respondents verbatim
  • Forces new data into an existing theme taxonomy when it genuinely doesn’t fit
  • Reports a score movement and a theme in the same study without checking whether they’re actually connected
  • Assigns severity/priority without stating the frequency + impact reasoning behind the tier
  • Codes themes by keyword/string matching instead of reading each comment for what it actually describes
  • Measures a theme’s frequency only against the whole sample when deciding severity — always check frequency within the affected score band first, since that’s what a diluted overall % can hide

Quick reference

SituationAction
n below recommended minimumScore it, widen CI, flag loudly
n so small the learning goal is unanswerableKick back to survey-architect
Score drop, no theme correlation foundReport the drop as unexplained, don’t force a story
Score drop + strong theme correlationState both together as one finding
New product, first studyEmergent coding
Existing product with prior studiesReuse taxonomy from benchmark file, extend only if needed
Standalone data, no spec fileSay so explicitly, identify instrument from data structure