survey-architect · diff
v1.2.1 to v1.1.1
30 added, 57 removed. Audit A to A.
---
name: survey-architect
- version: 1.2.1
+ version: 1.1.1
author: Personify Labs
description: >
Turns a research question into a validated, deploy-ready survey. Runs a
short intake to establish product context and learning goal, selects the
right instrument (SEQ, SUS, UMUX-Lite, SUPR-Q, NPS, CSAT, CES, PSSUQ, or a
custom item set) based on what's actually being measured, and outputs a
platform-native file ready to paste into Qualtrics, Typeform, or an in-app
survey component. Trigger when someone says "I need to survey users about
X," "what should I ask users after they do Y," "build me an NPS survey,"
"help me measure usability of Z," or pastes a vague research need and
wants it turned into an actual instrument. Entry point 1 of the research
- loop (see research-loop). Product/org-specific facts (which products
- exist, benchmark root path) live in ../config/product-context.yaml, not
- in this file — read it during intake rather than assuming a fixed
- product list. Bundles scripts/selection.py for the deterministic
- instrument lookup, sample-size floor, and intake/spec validation (run
- it, don't eyeball the table) and evals/ for regression testing — run
- evals/test_selection.py after touching selection.py, and check
+ loop (see research-loop). Bundles scripts/selection.py for the
+ deterministic instrument lookup, sample-size floor, and spec validation
+ (run it, don't eyeball the table) and evals/ for regression testing —
+ run evals/test_selection.py after touching selection.py, and check
evals/qualitative_cases.md after touching this file's prose.
tags:
- user-research
- ux-research
- surveys
- qualtrics
- nps
- sus
- supr-q
license: CC-BY 4.0
---
# Survey Architect
Turns "I need to know something about users" into a specific, validated,
deployable survey — not a generic questionnaire.
This skill does not open with a survey. It opens with an interview, because
the wrong instrument answers a question nobody asked.
-----
## Step 0: Intake — never skip this
Before picking an instrument, ask (as a short back-and-forth, not a form
dump):
- 1. **What product or surface is this for?** Check `../config/product-context.yaml`'s
- `products` list rather than assuming which products exist — this file
- is the one place that list lives, precisely so this skill stays
- portable across organizations without editing prose. If the named
- product isn't on the list, follow `unknown_product_policy`: ask
- whether it's new (add it) or a typo of an existing entry — never
- silently guess.
+ 1. **What product or surface is this for?** Name it specifically — a
+ product, a feature area, or a defined surface within one.
2. **What decision will this survey inform?** Not "understand users" —
something a stakeholder will actually do differently based on the
answer (ship/hold a feature, fix a flow, report a trend, justify
spend).
3. **Where in the user's experience does this sit?** A single task, a
full session with the product, the ongoing relationship with the
product, or one specific transaction (support ticket, checkout,
onboarding).
4. **Has this been measured before for this product?** If yes, check
`/research/_benchmarks/<product>.md` — reuse the same instrument and
wording so the new wave is comparable. Don't introduce a new
instrument mid-series without a stated reason.
If the answers to 2 and 3 don't line up (e.g., "I want to know if the
onboarding flow is easy" but the answer to 3 is "relationship"), point that
out before proceeding. Don't silently pick one.
Do not proceed to instrument selection until 1–3 are answered.
-----
## Step 1: Instrument selection
Classify the intake answers into a decision_type first — this part is
judgment, not arithmetic — then **run `scripts/selection.py`'s
`select_instrument(decision_type)`** rather than eyeballing the table
below, so the same decision_type can never silently map to a different
instrument on a different run. The table is here for the judgment call
of classifying the situation, not for hand-picking the instrument once
it's classified:
| Decision type | `decision_type` value | Where it sits | Instrument |
|---|---|---|---|
| Is this task doable? | `task_completion` | Single task, mid-test | SEQ |
| Is the product/feature usable overall? | `product_usability_full` / `product_usability_lite` | Full session, post-study | SUS / UMUX-Lite |
| How does our UX compare to the market? | `website_relationship_benchmark` | Website/app relationship | SUPR-Q |
| Are users loyal / would they recommend us? | `relationship_loyalty` | Relationship, quarterly | NPS |
| Were they happy with this specific interaction? | `transactional_satisfaction` | Transactional | CSAT |
| Was this support/self-serve interaction low-friction? | `transactional_effort` | Transactional (support) | CES 2.0 |
| Need diagnostic subscales? | `diagnostic_subscales` | Post-study, deeper | PSSUQ |
| Will they actually adopt this before it ships? | `adoption_prediction` / `adoption_prediction_lite` | Pre-launch prediction | TAM / UMUX-Lite |
Don't let the requester's phrasing ("I want NPS") override what they're
actually asking — if someone asks for NPS but the real question is "is
this specific flow usable," say so and recommend SEQ instead, with the
trade-off named. If `select_instrument` raises (nothing fits), build a
short custom item set and say explicitly that it has no external
benchmark, so the report later doesn't imply false comparability.
**Never modify the wording of a validated instrument** (SUS, SUPR-Q, UMUX,
UMUX-Lite, PSSUQ). A single reworded item breaks the benchmark comparison.
If the wording genuinely doesn't fit (e.g., "system" feels wrong for a
consumer app), swap the bracketed placeholder only — never the item
structure.
-----
## Step 2: Sample size
**Run `scripts/selection.py`'s `required_sample_size(purpose)`** for the
floor, rather than reciting the numbers from memory:
- `rough_benchmark` → 20
- `compare_two_conditions_between` → 213
- `compare_two_conditions_within` → 93
- `ongoing_tracking` → 30
These are MeasuringU's published rule-of-thumb bands, not a computed
power analysis — a true power calculation needs an effect size and
alpha/power assumptions most requesters can't supply at intake. If a
requester needs an actual power calculation for a specific comparison,
say that's a separate, more involved ask rather than quietly
approximating one here.
If the requester's expected reach won't hit the floor for what they asked
for, say so now — this is what feeds the small-sample flag downstream in
feedback-synthesizer. Don't let a study launch that's structurally
under-powered without the requester knowing going in.
-----
## Step 3: Trigger and frequency logic
Specify, as part of the spec, not left to the deploying team to guess:
- **Trigger event:** the specific action/moment that fires the survey
(task completion, session end, ticket resolution, N days after
signup) — never "on page load" or a calendar date alone
- **Frequency cap:** global cooldown (default: one survey per person per
14 days) plus per-instrument cap (relationship metrics like NPS: no
more than every 90–180 days)
- **Suppression rule:** don't re-survey someone who scored as a recent
detractor/low scorer until the issue is addressed
-----
## Step 4: Output
Produce a **platform-native, deploy-ready** artifact — not a generic spec —
matched to how it'll actually be shipped:
- **Qualtrics** → QSF-importable question block (question text, response
scale, scoring formula as a note)
- **Typeform** → JSON matching Typeform's question schema
- - **In-app** (a product from `../config/product-context.yaml`'s stack) → a
- small React component using the existing Vois design system
- conventions (route through vois-tokens/righter if UI copy beyond the
- validated item wording is needed — e.g., intro screen, thank-you
- screen)
+ - **In-app** → a small component matching the product's existing UI
+ conventions (route through vois-tokens/righter if the target uses the
+ Vois design system and UI copy beyond the validated item wording is
+ needed — e.g., intro screen, thank-you screen)
- **Unspecified platform** → ask which one before generating, don't
default silently
Along with the deployable file, always also write the structured handoff
- files (see research-loop for the full file structure) to
- `/research/<study-name>/00-intake.md` + `00-intake.json`, and
- `01-survey-spec.json`. The `.json` files are the canonical,
- machine-readable versions — the `.md` is the human-readable copy of the
- same facts, never a source of additional information the `.json`
- doesn't have. Before writing them, run `scripts/selection.py`'s
- `validate_intake_spec(intake)` and `validate_survey_spec(spec)` — if
- either returns any missing fields, fill them in before writing the
- files. This is what feedback-synthesizer and research-loop's state file
- read later — they should never have to re-derive the instrument from
- raw data or prose alone, and shouldn't hit a missing field that a
- two-line check would have caught.
-
- `00-intake.json` shape: `study`, `product`, `learning_goal`,
- `experience_moment`, `decision_type`, `instrument`, `required_n`, plus
- `prior_benchmark_reused` when Step 0's Q4 applies.
+ file (see research-loop for schema) to `/research/<study-name>/00-intake.md`
+ and `/research/<study-name>/01-survey-spec.json`, containing: product,
+ learning goal, instrument chosen, item text, scoring formula, required n,
+ trigger/frequency rules. Before writing it, run
+ `scripts/selection.py`'s `validate_survey_spec(spec)` — if it returns
+ any missing fields, fill them in before writing the file. This is what
+ feedback-synthesizer reads later — it should never have to re-derive the
+ instrument from the raw data alone, and it shouldn't hit a missing field
+ that a two-line check would have caught.
-----
- ## Rules
-
- Priority when rules interact: **STOP** > **MUST NOT** > **MUST** >
- **SHOULD** > **MAY** — see `ux-research/README.md` for the full
- precedence explanation. A lower-priority rule never overrides a
- higher-priority one.
+ ## What this skill never does
- - **STOP** if the answers to Step 0's Q2 and Q3 don't line up — surface
- the mismatch before picking an instrument, don't silently pick one.
- - **MUST NOT** skip intake because the requester already named an
- instrument.
- - **MUST NOT** modify validated item wording (SUS, SUPR-Q, UMUX,
- UMUX-Lite, PSSUQ) to "sound more natural."
- - **MUST NOT** recommend an instrument the intake answers don't support
- (e.g., NPS for a single-task usability question).
- - **MUST NOT** introduce a new instrument for a product with an existing
- benchmark series without flagging the break in comparability.
- - **MUST** state the required sample size before a study launches.
- - **MUST** produce a platform-native artifact, never a generic text spec,
- once a platform is named.
- - **SHOULD** ask which platform to target when none was specified,
- rather than defaulting silently.
+ - Skips intake because the requester named an instrument already
+ - Modifies validated item wording to "sound more natural"
+ - Recommends an instrument the intake answers don't support (e.g., NPS
+ for a single-task usability question)
+ - Launches a study without stating the required sample size up front
+ - Outputs a generic text spec when a platform was named
+ - Picks a survey platform without asking, when none was specified
+ - Introduces a new instrument for a product with an existing benchmark
+ series, without flagging the break in comparability
-----
## Quick reference
| Requester says | What to check | Likely instrument |
|---|---|---|
| "Is this new flow usable?" | Single task, mid-test | SEQ |
| "How's the product doing overall?" | Full session, post-study | SUS or UMUX-Lite |
| "Are people loyal to us?" | Relationship, quarterly | NPS |
| "Was support helpful?" | Transactional | CSAT or CES |
| "How do we compare to competitors' UX?" | Relationship, benchmarked | SUPR-Q |
| "Will people actually use this feature?" | Pre-launch | TAM / UMUX-Lite |
| Nothing above fits | Custom, one-off | Custom item set, no benchmark |