Design rationale

Why a demonstration needs something to demonstrate.

This tool is unvalidated on purpose and says so everywhere. But “unvalidated” is a statement about evidence, not an excuse for vagueness. The thing being demonstrated is a specific, reusable design: pseudonymous pre/post linkage, and the discipline of reading it without overclaiming.

Linkage unitcode + phase
Uniqueness ruleone record per code+phase
Code space328 ≈ 1.1 × 1012
Reviewed29 August 2026

The three ways to collect a questionnaire twice

Every pre/post instrument has to solve one problem: how do you know the second response came from the person who gave the first? There are only three broad answers, and each one trades measurement against exposure.

Collect anonymously and do not link
Safest for the respondent, and the resulting data cannot support a within-person statement of any kind. Differences between the “before” group and the “after” group are confounded with the fact that they are not the same people, and with who chose to answer each time.
Collect an identity
Linkage becomes trivial and permanent. The record is now directly attributable, and every export, backup, and downstream analysis carries a person's identity along with a sensitive disclosure about cannabis use — a disclosure that can matter for employment, insurance, custody, immigration, and licensing.
Collect a participant-generated code
The respondent invents a key, uses it twice, and keeps the mapping in their own head. The instrument gets linkage; the instrument never gets identity. This is what the sandbox implements.

Under the EU General Data Protection Regulation, replacing an identifier with a code is pseudonymisation — expressly defined as data that can still be attributed to a person with additional information held separately (Regulation (EU) 2016/679, Article 4(5)). Pseudonymised data remains personal data. The sandbox therefore uses the word “pseudonymous” everywhere and never the word “anonymous”.

How the linkage is actually implemented here

The mechanics are deliberately small enough to audit in a single sitting.

Code alphabet
Eight characters drawn from a 32-symbol alphabet with I, O, 0 and 1 removed, so a handwritten code does not transcribe into a different one. Generated codes come from crypto.getRandomValues, not Math.random.
Record key
A record is uniquely identified by code plus phase. Saving a second PRE under an existing code is refused rather than silently overwriting the first, because a silent overwrite destroys the earlier answer and leaves no trace that it existed.
Validation on read
Stored rows are re-validated every time the page loads and every time a file is imported: schema version, code shape, phase value, parseable timestamp, and every answer either null or an integer from 0 to 3. Rows that fail are quarantined and excluded, and the page says so.
Ordering
Pairing uses the declared phase, not the timestamp. A respondent who fills in the POST form first has mislabelled their own record, and the tool cannot detect that — which is why the phase radio is a deliberate, explicit choice rather than an inference.

A worked example: reading one pair

Suppose code K7QMR4TH has both records, and on the item “I feel prepared to describe the cannabis products, amount, route, frequency, and timing relevant to my care” the answers are:

One item from one pair. This is the entire evidentiary content of the observation.
ItemPrePostTransition
Conversation preparednessSomewhat disagreeAgreeSomewhat disagree → Agree

What may be said

“Under code K7QMR4TH, the recorded label on the preparedness item moved from Somewhat disagree to Agree between a record marked PRE and a record marked POST.” That is a complete and accurate sentence, and it is the strongest one available.

What may not be said

That preparedness improved. That anything caused it. That the difference is two units, or 66%, or worth anything on a scale — the response options are ordered labels, and the distance from “Somewhat disagree” to “Somewhat agree” is not known to equal the distance from “Somewhat agree” to “Agree”. Treating ordered labels as numbers is a specific, documented way to produce confident errors, including sign reversals in which the analysis reports the opposite of the underlying pattern (Liddell & Kruschke, 2018). Nothing in this tool converts a label to a number, and the export marks the responses as ordinal so a downstream reader inherits the same constraint.

The rival explanations, in order of how often they are ignored

  1. Response shift. The respondent's internal standard for “prepared” moved between the two answers. This can run in either direction, and it is worst precisely when the intervening experience was informative (Howard & Dailey, 1979; Sprangers & Schwartz, 1999).
  2. Testing effect. The first questionnaire told the respondent that amount, route, frequency, and timing are the things a clinician might want. The second answer is downstream of that, with or without anything else happening.
  3. Social desirability. “I intend to ask a healthcare professional” has an obviously approved answer, and the pull toward it is stronger once the respondent has worked out what the instrument is about (Krumpal, 2013).
  4. Regression to the mean. An unusually low first answer tends to be followed by a less extreme second answer with no intervening cause whatsoever (Barnett et al., 2004).
  5. Everything in the gap. There is no control condition, no randomization, no fixed interval, and no record of exposure. The design does not distinguish a class, a news article, a conversation with a pharmacist, and a bad week.

What would have to change for this to be research

Naming the gap is more useful than gesturing at it. To move from a workflow sketch toward something that could carry an evidentiary claim, a study would need, at minimum:

Until all six exist, the honest output of this design is a description of two labelled answers under one code — which is exactly what the tool prints.

Why it never touches the network

The browser-local guarantee is not a convenience decision. Cannabis disclosure is sensitive in ways that outlive the questionnaire, and the most reliable way to protect a record is not to transmit it. The site's Content-Security-Policy sets connect-src 'none', which means the page cannot open a fetch, an XMLHttpRequest, a WebSocket, or an EventSource even if some future edit accidentally tried to. There is no analytics script, no form endpoint, no font CDN, and no third-party embed to weaken it.

Exports and imports are handled entirely inside the page: files are produced with Blob and a local object URL, and imports are read with FileReader from a file you select. Both are file-system operations, not requests. The consequence to be honest about is the flip side: nothing is backed up, and clearing site data destroys the records permanently.

Return to the questionnaire → · Methodology and sources →