STANDARD OF TASTE

THE LAB · DATA MODEL

Everything this product keeps about you.

Not a summary of it — all of it. There is no database, no account, and no server-side record of any person: the entire model is a handful of keys in your own browser. Every key, cap and version below is read from the code that owns it rather than described here, so this page cannot quietly fall behind what the product actually does.

IT STORES ANSWERS, NEVER RESULTS

Nothing here holds a score. What is written is the raw answers you gave, in exactly the form a share link carries, and every number is recomputed from them on read. That is why a stored session and a shared link can never disagree — they are the same bytes through the same function — and why editing this storage by hand can only change which answers you claim to have given, not what they are worth.

Kept in this browser

Survives closing the tab and closing the browser. Lost if you clear site data, and invisible to any other device — there is no account tying them together. Everything here shares the gym. namespace, which is what lets forgetting this browser be one sweep rather than a list of keys somebody has to remember to update.

A finished session

src/lib/result-store.ts
gym.result.<instrument>

The raw answers from one completed sitting — the only thing this product records about a person. Threshold sessions are kept per ladder, so measuring pitch and then compression keeps both.

payload
the answers themselves, in exactly the form a share link carries
poolVersion
which version of the item pool those answers were given against
savedAt
when the sitting finished
v
the envelope format, currently 2

No result is stored — only answers. Every number is recomputed on read, so a stored session and a shared link cannot disagree, and editing the store by hand can only change which answers you claim to have given. Answers recorded against an older pool are dropped rather than scored against a pool that has since been reordered.

The history behind an arc

src/lib/result-store.ts
gym.result.threshold.<ladder>

Sittings in time order, so a later session can be compared against the same person's earlier ones. This is what makes the retest arc possible.

sessions[]
up to 24 sittings, oldest evicted first
(read order)
an arc pools at most 2 sittings a side, weighted toward the newest

It cannot be ranked. There is no way to ask this store for a best, a maximum or a personal record — a history invites 'your best result', which is selection on the answer and the exact bias the Prestige Test exists to measure. Beyond the cap the oldest sittings are dropped, and nothing on screen says they existed.

The retest gate

src/lib/retest-cooldown.ts
gym.result.threshold.<ladder>

Whether 7 days have passed since this family was last measured. A retest taken too soon measures memory rather than hearing, so the gate is about validity, not about withholding anything.

(nothing of its own)
the gate owns no key — it asks the session history above when this family was last measured, so finishing a session cannot write two records that disagree
gym.lastCompleted.<family>
retired. Read only when the session store has nothing, which means a browser that finished a session before the store existed. Nothing writes it, so it drains on its own

Device-local, and anyone who wants to can clear it. That is fine: it catches the person who forgot, not the person who insists, and the screen explains before any retest why an early number is worse.

Kept for one tab

Gone the moment the tab closes. None of it is a profile, and none of it is joined to a session record.

Which variant this tab is seeing

src/lib/experiment.ts
vc_arm · vc_voice · vc_pb · vc_attr

A coin flip held still for the length of one visit, plus where the visit came from, so a reload cannot re-randomise a person and double-count them.

vc_arm
which onboarding variant this tab was assigned
vc_voice
which voice variant the reading is written in
vc_pb
what the visitor said they believed before measuring
vc_attr
the referral parameters the visit arrived with

One tab only, and gone when it closes. Nothing here is a profile and none of it is joined to a session record.

Your argument with a reading

src/content/reading/state.ts
reading_choices

Which lines of a reading you rejected and which offered reading you chose, per illustrative listener, so the prompt stays as you left it if you follow the link to a hearing test and come back.

<listener>.rejected
the lines you rejected; they drop out of the prompt
<listener>.chosen
per line, the reading you picked: a, b, or neither

One tab only, and gone when it closes. It records choices about a fictional listener's plays, never your own listening, and nothing here leaves the browser.

What gets sent away

25 EVENTS · 6 SURFACES

Usage events — the one thing here that does leave your browser. Most carry only the fact that something happened. 2 of them carry your actual answers too, and those are marked ANSWERS below. That is deliberate rather than accidental: a set of real responses is the thing this project is trying to build, and it does not have one yet. Every event the code emits is listed here, because the page is built from the same registry the code is held to — an event that fires without appearing here breaks the build.

Site-wide

2

Fires anywhere, including inside the gym.

landing_view
the homepage mounts
client_error
a route error boundary catches — site-wide, including the gym

The Prestige Test

11

The flagship instrument, and the only path whose every step already emits — which is why the funnel specification is written against it.

bias_frame_view
the Hume frame is shown on /bias
bias_start
the blind pass begins
bias_session_restored
an interrupted session is resumed from its saved ratings
bias_blind_complete
the final blind rating is given and the pass ends
bias_labeled_complete
the final labelled rating is given; the verdict is computed
bias_result
ANSWERS the verdict is computed — the interim D6 record, carrying the raw ratings
bias_debrief_view
the mandatory debrief is shown, disclosing the swapped labels
bias_share
the share control is used on the reveal
bias_result_view
/bias/result renders — the taker's own return or someone else's link
bias_locked_tier_tap
a locked instrument is tapped from the gym floor
gym_machine_tap
a reveal's onward card to another instrument is taken, carrying from and to

The Delicacy Trials

6

Same-moment A/B trials, each committed by a confidence tap.

delicacy_frame_view
the Delicacy frame is shown
delicacy_start
the practice trials are begun
delicacy_trial_complete
one scored trial is committed by the confidence tap
delicacy_result
ANSWERS all trials answered; the score is computed and banked
delicacy_share
the share control is used on the reveal
delicacy_result_view
/delicacy/result renders

The Threshold Test

3

The adaptive staircase, which reports a sensitivity in physical units.

threshold_start
a staircase session begins, carrying its family and source
threshold_complete
the staircase converges and reports a threshold
threshold_share
the share control is used on the threshold result

The Ranking Test

2

The critic-ranked listening set. These two events are the only record of a sitting that leaves the device; the answers themselves stay in the browser that gave them.

spread_start
the Ranking Test's frame is dismissed and listening begins
spread_complete
all six works are rated and the two spreads are computed

Share primitives

1

Fired by the share buttons themselves, whichever surface they sit on.

share_download
a card image is downloaded

From a tap to a statistic

A list of events says what is recorded. It does not say how something you did becomes a number you read. Each row follows one action the whole way — and every step of it is checked against the module that owns it.

You rate a clip out of ten, having been told nothing about it.

RECORDED AS
bias_blind_complete
ANSWER KEPT IN
gym.result.bias → payload.blind
SCORED BY
src/engine/bias.ts
ENDS UP AS
sway_pct

You rate the same clips again, this time with a name attached.

RECORDED AS
bias_labeled_complete
ANSWER KEPT IN
gym.result.bias → payload.labeled
SCORED BY
src/engine/bias.ts
ENDS UP AS
sway_share

You choose which of two clips is the untouched one.

RECORDED AS
delicacy_trial_complete
ANSWER KEPT IN
gym.result.delicacy → payload.picks
SCORED BY
src/engine/delicacy.ts

…and, in the same tap, how sure you were.

RECORDED AS
delicacy_trial_complete
ANSWER KEPT IN
gym.result.delicacy → payload.picks
SCORED BY
src/engine/calibration.ts
ENDS UP AS
brier

…and, across all of them, how many different scores you were willing to give.

RECORDED AS
bias_blind_complete
ANSWER KEPT IN
gym.result.bias → payload.blind
SCORED BY
src/engine/comparison.ts
ENDS UP AS
degrees_used

You rate six works a critic ranked, having been told nothing about them.

RECORDED AS
spread_complete
ANSWER KEPT IN
gym.result.spread → payload.ratings, payload.recognised
SCORED BY
src/engine/spread.ts

You say whether you could hear the flaw, forty to eighty times.

RECORDED AS
threshold_complete
ANSWER KEPT IN
gym.result.threshold.<ladder> → payload.answers, payload.seed
SCORED BY
src/engine/threshold-fit.ts
ENDS UP AS
nothing in the dictionary

This one ends outside the dictionary, and deliberately. It produces a sensitivity in physical units — cents of detune, milliseconds of smear, kbps — which is a fact about one person on one evening, not a statistic describing the instrument. The dictionary holds the second kind. On most sittings it declines to print a single number at all and reports a band instead, because a point estimate from a noisy measurement is a claim the measurement cannot support.

Back to the Lab

Methodology · The Prestige Test · The Delicacy Trials · The Threshold Test · The Ranking Test