Carrier — A Researcher's Guide
A guide for researchers — psychologists, social scientists, HCI researchers, and study designers — who want to use Carrier to run interactive experiments involving any combination of human participants, LLM chatbots, scripted chatbots, and Claude Agents. Part I introduces the four systems out of which every Carrier experiment is built; Part II covers the day-to-day mechanics of running a study.
Overview
Who this guide is for
This guide is written for researchers — psychologists, social scientists, HCI researchers, and study designers — who want to use Carrier to run interactive experiments involving any combination of human participants, LLM chatbots, scripted chatbots, and Claude Agents.
It assumes you are comfortable thinking about experiments in the usual methodological terms — conditions, manipulations, between- vs. within-subjects designs, counterbalancing, blinding, attention checks — but not that you have any prior experience with the platform or with the engineering vocabulary used internally to describe it.
Whenever Carrier uses a term that has a recognisable counterpart in research methodology, the first time you meet it we will name the counterpart explicitly.
Overview
How to read this guide
The guide has two parts.
Part I — Designing an Experiment is conceptual. It introduces the four systems out of which every Carrier experiment is built. Each chapter begins with the research problem the system solves, then names the building blocks Carrier offers, and finally describes the design decisions you make as a researcher. Short builder walkthroughs (still images and short GIFs) and worked examples accompany each chapter.
Part II — Operating the Platform is operational. It covers the day-to-day mechanics of running a study: activating an experiment, monitoring participants, exporting data, managing accounts. It is short and assumes you have read at least the relevant chapters of Part I.
Appendices at the end provide a glossary, the type-by-role compatibility matrix, and quick-reference indexes for segment types and trigger types.
Overview
The four systems
A Carrier experiment is built out of four interlocking systems. Each answers one of four design questions:
| System | Question | What it gives you |
|---|---|---|
| Chamberlines, chambers, segments | What is the shape of a participant's journey? | A way to specify conditions, group participants, and lay out the activities they move through. |
| Roles | Who takes part, and in what capacity? | Three roles (communicator, mediator, processor) that any human, LLM chatbot, scripted chatbot, or agent can occupy. |
| Variables | What do we know about each participant, and how should that change their journey? | A way of carrying participant attributes through the experiment and using them to filter matches, gate visibility, and personalise instructions. |
| Triggers | How should non-human participants behave? | A rule-based system that defines, for every non-human participant, the conditions under which they speak or act. |
Schematically, they fit together like this:
slots / roles
slots / roles
The systems are introduced in this order — shape, then occupants, then information, then behaviour — because each layer presupposes the one before it. Once you have read all four chapters you can return to each independently.
Overview
A note on terminology
| Carrier term | Closest research counterpart |
|---|---|
| Chamberline | Condition / experimental arm |
| Chamber | A timed grouping of matched participants |
| Segment | An activity / phase within a chamber |
| Slot | A position in a chamber to be filled by a participant |
| Run | One participant's complete pass through the experiment |
| Chatroom | The live instantiation of a chamber for a matched group |
| Variable | An attribute attached to a participant (from a survey, an assignment, or the system) |
| Trigger | A condition–response rule that governs a non-human participant |
These mappings are not strict — see the glossary in Appendix A for nuance — but they will get you most of the way.
Overview
A note on non-human participants
Carrier distinguishes three kinds of non-human participant. The distinction is important because they differ in what they can do, how reproducible they are, and which research designs they serve best.
- LLM chatbots are language-model-driven participants (OpenAI, Anthropic, Google, or any compatible provider) that converse. On each turn the model is given the conversation so far and a system prompt, and it produces a chat message. That message is the entirety of its output. LLM chatbots are open-ended and naturalistic, but they vary from session to session. Use them when you want behaviour that reads and reacts to conversation in an unconstrained way.
- Scripted chatbots are rule-driven participants whose responses are pre-written. A scripted chatbot is configured with a set of triggers (§5) — rules of the form “when keyword X is uttered, send one of these three sentences” — and produces nothing else. Scripted chatbots are deterministic and replayable: the same input sequence produces the same outputs across sessions. Use them when you want behaviour that is reproducible, auditable, and identical across participants.
- Agents are autonomous participants built on Anthropic's Claude Agent API. Unlike a plain LLM chatbot, an agent does not just answer from the conversation history — it has access to a vocabulary of built-in tools it can invoke on its own initiative to gather information or take action: reading files from a configured document area, running commands, browsing the web. The agent chains these tool calls across multiple steps without being asked between each step, and produces its eventual chat message grounded in what it read or computed. Each agent is typically scoped to a particular document area — the study materials, a cited corpus, a specific dataset — and behaves as the experiment's resident expert on that material. Use an agent when you want a non-human participant that retrieves and reasons over a body of material during the conversation, not just one that talks from training-time knowledge.
All three kinds can take any of the three roles described in §3, with one exception: scripted chatbots cannot serve as processors (see §3.5). The relationship between kinds and roles is summarised in the type × role matrix in §3.1 and again in Appendix B.
When this guide says “chatbot” without qualification it means either an LLM chatbot or a scripted chatbot. “Agent” — capitalised or not — means specifically a Claude Agent. “Non-human participant” is the umbrella term that covers all three.
A note on terminology overlap: any LLM-driven participant (an LLM chatbot or an agent acting in a mediator role) can also be configured to emit Carrier intervention actions — disable a participant's input, prompt someone, highlight a message — alongside its chat message. These intervention actions are a Carrier-specific structured-output mechanism, not the same thing as a Claude Agent's built-in tools. §5.5 separates the two carefully; for now, it is enough to know that “tools the agent uses to read files and browse” and “intervention actions a mediator chooses to fire” are different channels.
Part I · §1. Chamberlines and Chambers
The outer shape of a Carrier experiment: chamberlines isolate the condition and chambers isolate the matched group. The activity is the subject of §2.
§1 · Chambers
1.1 The shape of a Carrier experiment
Every Carrier experiment has the same nested shape. A participant who opens the experiment URL is assigned to a chamberline — the condition they will experience. Their chamberline is an ordered sequence of chambers, each of which is a small group of participants (human, AI, or both) who are matched once and remain together for the duration of that chamber. Inside each chamber, participants progress through an ordered list of segments — the activities that constitute the chamber, of which a real-time conversation is the most common.
This three-level structure is the backbone of Carrier:
The three levels exist to separate three concerns that are easily entangled when designing an interactive study:
- Chamberlines isolate the condition. Different chamberlines represent different experimental arms; a participant sees exactly one.
- Chambers isolate the matched group. Within a chamber, who you are with does not change.
- Segments isolate the activity. Within a segment, what you are doing does not change.
The remainder of this chapter introduces each level in turn.
§1 · Chambers
1.2 Chamberlines: the unit of condition
1.2.1 Why chamberlines
In a typical lab study you might compare two or three experimental conditions — say, high-anonymity versus low-anonymity discussions of a controversial topic. A chamberline is Carrier's representation of exactly that: a complete journey that one group of participants will take through the experiment. Multiple chamberlines in the same experiment correspond to multiple between-subjects conditions.
A participant is assigned to a single chamberline at the start of their session and does not leave it. Within-subjects comparisons (the same person experiencing two manipulations) are typically built inside a chamberline, by sequencing chambers that differ along the manipulated dimension; between-subjects comparisons (different people in different manipulations) are built across chamberlines.
1.2.2 Assigning participants to chamberlines
Carrier offers four assignment methods, configured at the experiment level:
| Method | What it does | When to use |
|---|---|---|
| Random | Each new participant is allocated to a chamberline uniformly at random. | The default for simple between-subjects studies. |
| Counterbalance | Carrier maintains running counts and assigns each new participant to the currently smallest chamberline. | When you want equal n per condition and cannot wait for the law of large numbers. |
| Survey-based | A field from the global pre-survey is read at assignment time and used to choose the chamberline. | When the condition depends on a participant attribute that they declare themselves (e.g. native language, political identification). |
| Fixed | All participants are placed in the same named chamberline regardless of anything else. | Pilots and demonstrations; reproducing exactly one condition. |
Survey-based assignment is the most flexible. The global pre-survey runs before chamberline assignment, so any response collected there is available as a routing variable; this is the same mechanism described in §4.
1.2.3 What lives on a chamberline
A chamberline is, formally, a name plus an ordered list of chambers plus optional assignment criteria. There is no further configuration at this level — chamberlines are intentionally thin, so that a researcher can read the shape of an experiment by scanning the chamberline names and their chamber sequences in the builder.
A new experiment already carries one chamberline, so the researcher clicks “Add Chamber Line” once to get a second. They click each chamberline’s name in turn and rename them “High Anonymity” and “Low Anonymity”, then click the empty canvas to return to Experiment Settings and set Chamberline Assignment to “Random”. The two chamberlines now stack on the canvas, each ready to receive chambers.
§1 · Chambers
1.3 Chambers: the unit of matched group
1.3.1 Why chambers
A chamber is the basic unit of togetherness in Carrier. Once a participant enters a chamber, they are matched with the other occupants of that chamber and they stay together until the chamber ends. Within a chamber, the cast does not change.
This is a deliberate constraint. Many interactive studies depend on participants having a stable conversational partner across multiple tasks — a discussion followed by a joint ranking, for example, or a chat followed by a rating of the other person. In Carrier, those sequential activities belong in the same chamber and share its participants. Crossing a chamber boundary, by contrast, dissolves the group: the next chamber re-matches its occupants from the pool of participants who have reached that point.
1.3.2 Matching at the chamber boundary
Matching happens once, at the start of each chamber. Participants who finish the previous chamber (or, for the first chamber in a chamberline, who have completed the global pre-survey) enter a waiting pool. As soon as enough participants are present to fill the chamber's required slots, the chamber begins.
A slot is a description of the kind of participant the chamber needs. Each slot has a type (human, LLM chatbot, scripted chatbot, or agent) and a role (communicator, mediator, or processor; see §3). For human slots, matching can additionally require certain variable values — for example, that the chamber contain one self-identified novice and one self-identified expert. Variable-based matching is the topic of §4.
When a matching attempt does not assemble enough participants within a configured interval, Carrier applies a chamber-level fallback policy. The fallback is part of the chamber's configuration; common choices are to keep the participant waiting, to fill the missing slot with a default agent, or to end the run gracefully with a completion code. The choice is the researcher's, not the participant's.
1.3.3 What lives on a chamber
A chamber carries several pieces of configuration:
- A name and identifier, used in the dashboard and in exported data.
- A communication channel:
text,audio, orvideo. The channel determines what the chat segment looks like and what data is recorded (transcripts, audio files, recorded video, or any combination). - A slot definition, listing the roles to be filled and the type each slot expects.
- An ordered list of segments, described in §2.
- An optional pre-survey shown to each participant before they enter the chamber, and an optional post-survey shown after they leave.
- A maximum participant count — the total number of slots.
Chamber pre- and post-surveys are distinct from the experiment's global pre- and post-surveys. The global surveys run once per participant, at the very beginning and end of the run; the chamber surveys run once per chamber. Researchers typically use the global surveys for demographics and consent, and the chamber surveys for state measures that need to be taken before and after each manipulation.
The researcher selects a chamber, renames it to “Deliberation” in the configuration panel, and drags Human Participant from the Library into the chamber’s Communicators lane twice. The chamber card’s occupancy badge updates to two. (Slots are added by dragging an entity into a role lane, not through an “add slot” button; the communication channel is a setting on the chat segment, not on the chamber — see §1.4.)
Segments — the activities a matched group moves through inside a chamber, and the parameters of each — are the subject of §2. Roles are the subject of §3.
Part I · §2. Segments
The unit of activity: what a matched group actually does, one activity at a time, and how each of the fourteen kinds is configured.
§2 · Segments
2.1 The segment system
A chamber's segments are its inner timeline: the participants are already matched, and now they move together through an ordered series of activities. Each segment is a single, self-contained activity — showing a slide, holding a conversation, voting on options, ranking items, watching a video, or completing a short embedded survey — with its own timing and transition rules.
Segments are deliberately fine-grained. A “thirty-minute deliberation” study in Carrier is usually not a single thirty-minute chat segment but a sequence: a slide introducing the topic, a timer giving participants a moment to think, a chat segment for the deliberation itself, a ranking segment to record the group's collective answer, and a short survey at the end. Each piece is configured separately, recorded separately, and can be skipped, repeated, or replaced without touching the others.
§2 · Segments
2.2 The catalogue of segment types
Carrier ships fourteen segment types. The Compatible with AI column marks the types a non-human participant can contribute to — LLM chatbots, scripted chatbots, or agents. The conversational and choice-based types can; the read, listen, wait and integrity types cannot, because there is nothing for a non-human participant to do. The Embeddable column marks the types that can run as an overlay on a chat rather than taking the screen to themselves, which is the subject of §2.3.
| Type | What participants do | Compatible with AI | Embeddable |
|---|---|---|---|
instruction | Read formatted instructions and click Continue. | — | ✓ |
slide | Step through a deck of researcher-designed slides, one at a time. | — | ✓ |
media | Watch or listen to a clip. | — | ✓ |
pdf | Read a document, optionally beside a live chat. | — | ✓ |
timer | Wait, with or without a visible countdown. | — | ✓ |
survey | Answer an embedded Survey.js questionnaire. | — | ✓ |
input | Write a free-text answer. | ✓ | ✓ |
selection | Choose from a set of options. | ✓ | ✓ |
ranking | Drag a set of items into an order. | ✓ | ✓ |
chat | Hold a real-time conversation with the other occupants. | ✓ | — |
social | Browse a mock social feed — comment, reply, like or vote, follow. | ✓ | ✓ |
task | Complete a custom interactive task. | ✓ | ✓ |
process | Run an LLM processing step between activities. | ✓ | — |
attention-check | Pass a survey- or camera-based attention check. | — | — |
Each type has its own reference entry below, from §2.6 onwards: what the participant sees, what it is for, and every parameter its configuration panel exposes.
§2 · Segments
2.3 Standalone and embedded segments
Most segment types occupy the participant's entire screen for the duration of the segment. We call these standalone segments: they take their turn in the chamber's timeline, run to completion (or timeout), and then yield to the next segment.
Most non-chat segment types can additionally be configured to run embedded inside a chat segment. An embedded segment is rendered as an overlay on top of an ongoing chat, so that participants can act on it — vote, rank, write, watch a clip, read an instruction — without leaving the conversation. The chat continues to record messages in the background, and the embedded child appears only when its start trigger fires.
The activity replaces the screen. The conversation is not visible while it runs, and resumes when it ends.
The activity is laid over the chat. Messages keep arriving behind it, and the group answers without leaving the discussion.
selection segment in both display modes. Only the second
one keeps the conversation on screen — which is the whole reason the
embedded mode exists.
Embedded display is supported for the selection, ranking,
pdf, social,
input, task, slide, instruction,
timer, media, and survey segment types. Two
segment types are excluded outright: chat (it is the parent container; you
cannot embed a chat inside a chat) and attention-check (no embedded
renderer — attention checks always run standalone). A third,
process, is offered the setting by the builder but has no embedded
renderer either, so an embedded process segment shows the
participant an error rather than a step — leave it standalone. On screens narrower than
900 px the runtime additionally falls back to standalone rendering for
every segment, so the layout never breaks on mobile.
Embedded segments are useful when the activity is part of the conversation rather than an interruption to it. Two examples:
- Periodic polling during a deliberation. Ask participants to vote at three points (after one minute, after three minutes, after five minutes) while they continue talking. Each vote is a separate embedded
selectionsegment with a different embedded-start trigger. - Pacing a ranking activity to chat progress. Show a ranking overlay only once the conversation has produced enough material to rank — for example after a fixed number of chat messages, or after a fixed time offset from the start of the chat.
An embedded segment adds three configuration parameters beyond its standalone equivalent:
| Parameter | What it does |
|---|---|
| Embedded start | When the overlay first appears, relative to the parent chat: immediately, after N seconds, after N chat messages, or after the previous embedded sibling ends (chained, with an optional delay). |
| Embedded stop | When the overlay closes: as soon as the participant submits / clicks Next (the default), or after a hard N-second timeout. |
| Embedded completion behaviour | What happens when the embedded child finishes: dismiss it (continue chatting), end the parent chat, lock the overlay so it cannot be reopened, or minimise it as a badge. |
A chat segment with one or more embedded children may use an additional transition mode, embedded-complete, which ends the chat when every embedded child has completed. This is the canonical way to build a chat segment whose end is gated on the group having voted (or ranked, or read the instruction), rather than on a fixed duration. An optional fallback timeout on the chat itself is a safety net for chains whose start triggers might never fire — for instance an after N messages trigger if the participants never reach that message count.
For an attention-check segment (or any activity that needs the
participant's complete attention), keep the display mode at standalone
— the chamber timeline yields to it the same way it would for a survey or
instruction.
The researcher selects a chat segment in a chamber, adds a selection segment immediately after it in the segment timeline, and changes the selection segment's display mode to “Embedded (overlay on chat)”. They set the embedded start to “after 60 seconds” and the completion behaviour to “minimise”. The selection segment now appears in the timeline as an indented child of the chat, with a small “embedded” badge.
§2 · Segments
2.4 Inline and split view
An embedded segment can occupy the chat in one of two ways, chosen with the Embedded Layout setting.
Inline renders the child as a card appended into the message stream itself. The card scrolls with the conversation, in the position the conversation had reached when it appeared. This suits short interruptions — a vote, a one-line question — where the activity is a moment in the discussion rather than a task beside it.
Split docks the chat into a column and gives the child its own
pane alongside. Nothing scrolls away: participants read, watch or work in one
pane while talking in the other. This is the layout for material that has to stay
on screen — a document being discussed, a task being performed
collaboratively. The pdf segment is the only type that defaults to
it, because reading a document while discussing it is the case it exists for.
The card sits in the message stream and scrolls away with it. Suits a moment in the conversation — a quick vote, a one-line question.
The chat docks into a column and the activity gets a pane of its own. Nothing scrolls away — for material that must stay on screen.
Card width and card height size the child as a percentage of the available area, defaulting to 75 % and 35 %.
An embedded segment degrades rather than failing, which means a misconfiguration shows up as a participant seeing the wrong thing rather than as an error at save time:
- Split without a chat parent falls back to inline. The split pane mounts against an actual chat segment; if the embedded child does not follow one, the layout quietly reverts.
- Narrow viewports fall back to standalone. Below roughly 900 px the child takes the whole screen instead of overlaying the chat, so a participant on a phone sees a different sequence from one on a laptop.
- Mediators and observers see the standalone form. The overlay is rendered for the participants doing the activity, not for those watching it.
§2 · Segments
2.5 Timing, transitions, and participant pacing
Every segment has its own timing and transition rules, which together determine how long participants spend on it and how they advance. These are the four parameters that matter most:
- Duration — the maximum time the segment may run. If left unset, the segment has no automatic deadline.
- Minimum duration — the earliest moment at which a participant may advance. This is the standard way to enforce a floor on engagement (a “read for at least 30 seconds before continuing” instruction slide, for example).
- Warning time — how long before an auto-advance the participant is warned. Useful to prevent surprise transitions in long segments.
- Transition mode — the rule for moving on:
| Mode | Description |
|---|---|
| Auto | The segment advances on its own when the duration elapses. |
| Manual | Each participant advances when they click Continue. |
| Sync | The segment advances only when every participant in the chamber is ready. Keeps the group in lock-step. |
| Host | The experimenter advances the segment from the dashboard. |
| Embedded-complete | (Chat segments only) Advances when every embedded child has completed. |
The pacing choice has substantive consequences. A sync transition gives participants the experience of a shared rhythm, but it also means that the slowest participant determines the group's pace, which can be frustrating in long studies. A manual transition lets each participant move at their own speed, but it can break the group character of a chamber if used for the chat segment itself. A host transition is most useful during pilot testing — the experimenter can step the group through the timeline by hand to debug pacing.
§2 · Segments
2.6 The slide segment (deck)
A slide segment is a deck: an ordered set of
slides that the participant steps through one at a time. That is the whole
distinction from instruction, and it is worth stating before
anything else — an instruction is one thing to read and acknowledge;
a slide segment is a sequence to present. A study brief with a Continue
button underneath belongs in instruction
(§2.7), which has the better authoring
surface for prose. A stimulus that unfolds across four screens — a
scenario, then a chart, then the manipulation, then the question framing
— belongs here, as one segment rather than four.
The participant sees one slide filling the activity area, with the usual chamber chrome around it — segment progress indicator, countdown clock and participant list in the sidebar, subject to the chamber's own settings. Beneath the slide sits a Next button on every slide but the last, and a Back button beside it on every slide but the first, and then only if the segment allows going back, which it does not by default. On the last slide the deck's Next disappears and the segment's own Continue takes its place, so leaving the deck and leaving the segment are never two separate clicks. That chamber progress indicator counts segments: there is deliberately no counter for the deck itself, so the participant is not told how many slides remain, because knowing changes how carefully the early ones are read. If a slide sets a minimum dwell, the button it holds renders disabled with “Available in 12s” counting down beside it. There is nothing to submit inside a slide: the participant reads or watches, then moves on.
Slides are built in the Deck Builder, a modal opened from the
segment's configuration panel. The left column is the slide list — add,
duplicate, reorder, delete — and the right pane holds the selected
slide's layout settings and its blocks. A block is one piece
of content of a declared type: a heading, a text block
written as Markdown in the same full-screen editor the
instruction segment uses, an image or video
chosen from the experiment image library or given by URL, or an html
block injected into the page exactly as authored. Blocks stack in the order
listed, and nothing is written back to the experiment until Save deck.
<h2>Round 1 — the proposal</h2>
<p>Your group has <strong>£10,000</strong> to allocate between three bids.</p>
<blockquote>{{var.first_name}}, you have been asked to speak first.</blockquote>
{{var.first_name}} has resolved to this participant's
value. Note what an Align: centered slide does to the
<blockquote>: it is centred with everything else rather than
set as a pull quote, because the slide's alignment governs the text inside it
too. This is a one-slide deck, so there is no Next — the
Continue button comes from the segment's transition settings. The
sidebar — segment name, duration, participant list — is chamber
chrome, not part of the slide.
Heading, text and HTML blocks support {{var.X}} tokens, resolved
per participant when the segment begins (see
§4.5) and applied block by block, which
is how one deck serves as a personalised briefing across an entire sample.
Every slide's tokens are resolved at that same moment, not when the slide is
reached, so a token on slide five carries the value it had when the participant
entered the segment.
Two settings live on the segment itself; everything else that shapes the deck is per slide, edited in the Deck Builder.
| Parameter | What it controls |
|---|---|
| Open Deck Builder | Opens the authoring modal (config.slides). The panel beneath the button reports the deck's size — “4 slide(s) · 11 block(s)” — so the shape of a deck is readable without opening it. Nothing typed in the modal reaches the experiment until Save deck; Cancel and the backdrop both discard it. |
| Allow participants to go back | Renders a Back button beside Next on every slide but the first (config.allowBack). Off by default, on the reasoning that revisiting a stimulus contaminates a controlled sequence. Turning it on does not hide the revisits: the view log records every entry, so a re-read is visible in the data rather than silently averaged into one dwell. |
| Per slide · Layout | Stacked (blocks in one column), Media left or Media right (the slide's first image or video block in a pane beside the rest, stacking below 900 px). A slide with no image or video block renders the split layouts as stacked rather than drawing an empty pane. |
| Per slide · Width | Column (a capped reading column, the default) or Full width (edge to edge). |
| Per slide · Align | Centered (the default) or Left. This positions the block column and sets the text alignment inside it to match — unlike the instruction segment's Content Alignment, which moves the column without touching the prose. |
| Per slide · Background | A CSS colour or an image URL, applied to the whole slide area — a colour as the background colour, a URL as a cover image. Values that are neither a colour nor an http(s) URL are ignored rather than passed through to the stylesheet. |
| Per slide · Minimum dwell (seconds) | Holds this slide's button disabled, with “Available in…” counting down beside it, until the time has elapsed. It gates the deck's Next on any slide but the last, and the segment's Continue on the last. It is a floor on this slide alone, counted from the moment the slide appears, and separate from the segment's own Minimum duration, which is counted from the moment the segment starts and gates leaving it. On the last slide both hold the same Continue, and the participant waits for whichever expires later — they do not add up. |
| Per slide · Advance when the clip ends | Offered only on a slide that has a video block. When the clip finishes, the deck moves to the next slide — or, on the last slide, ends the segment. It waits out this slide's minimum dwell first in either case, and on the last slide the segment's minimum duration as well, matching what the buttons beside it would allow. Unticked, the clip simply stops and the participant clicks on. |
| Block · Heading | A line of text at level H1, H2 or H3. Escaped, so it is text rather than markup. |
| Block · Text | Markdown, written in the same full-screen editor the instruction segment uses — formatting toolbar, live preview, and an Insert variable picker that lists every experiment variable as {{var.key}}, split into Available here and a greyed-out Not yet defined group. Availability is computed for this segment's exact position. |
| Block · Image / Video | A URL and an alt text, which becomes the image's alt. Beside the URL field sit a dropdown of the experiment's image library and an Upload… button that adds files to it — the same library the social feed segment uses (§2.16); anything uploaded from either place is available in both. Only http and https URLs are rendered; anything else shows Invalid media URL. in place of the stimulus rather than being embedded, and a block left without a URL shows No media URL configured. |
| Block · HTML | Injected into the participant's page as written: tags are not escaped, so an <img>, a table, a styled vignette or an <iframe> pointing at an external stimulus all work exactly as authored. It is the escape hatch for a screen that has to look like something specific, and it is where a slide authored before decks lands when it is converted. |
| Display Mode | Standalone (replaces the screen) or Embedded (a card inside a running chat), stored as config.displayMode. Choosing Embedded reveals the start / stop / completion trigger fields and hides the Timing & Transition card entirely, because an embedded slide is paced by its triggers instead. |
| Timing · Duration and Transition · Mode | Shared fields, but load-bearing here: reaching the end of the deck does not end the segment by itself, so duration and transition mode are still what ends it — the one exception being a last slide whose video is set to advance when the clip ends. See §2.5. |
There is no Data Sharing control on a slide, and no results display. Those appear only on segments that collect a response from the participant, which a deck does not — though it does record how the participant moved through it.
Every slide change writes a view log to the segment's
submission: one entry per visit, carrying the slide's id and position, the
millisecond offsets at which it was entered and left, and the resulting dwell.
A slide visited twice appears twice, so revisits under Allow participants
to go back are countable rather than merged. Alongside it sit
slidesViewed, deckSize and deckCompleted,
the last recording whether the participant actually reached and left the final
slide. All of it arrives in the Segment Submissions section of the data
export (§6.5), with the log itself as a
JSON string in the submission's data.
A slide segment authored before decks existed keeps working untouched: it is
read at run time as a one-slide deck, its old content becoming
a single block and its old layout mapping onto the new width and
alignment. There is no migration and nothing to re-save — the old fields
stay on disk exactly as they are, and the segment's configuration panel says so.
They stop being read the moment the segment is saved from the Deck Builder,
which is what gives such a segment a slides array.
2.6.1 Standalone and embedded display
A slide runs standalone by default, and can be embedded inside a chat segment — it is one of the types the builder offers the Display Mode dropdown for. Embedded, it defaults to the inline layout, appearing as a card in the message stream with its own Continue button and, optionally, pinned to the top of the chat while it is active; the alternative split layout mounts the content in a pane beside a docked live chat. Two conditions gate this. The embedded segment must sit immediately after the chat segment it belongs to in the chamber timeline, which is how the builder decides whose child it is. And on viewports narrower than 900 px the runtime ignores the embedded setting and renders the slide standalone, so a participant on a phone sees a full-screen slide where a desktop participant sees a card in the conversation — worth knowing before treating an embedded slide as a controlled stimulus.
A deck works inside the card as it does full-screen: the slides replace one another in place, and the card's Continue stays hidden until the last slide is reached, so one click cannot skip the rest of the deck. The card's timing is the exception noted above — the Timing & Transition controls are replaced by the embedded triggers — but per-slide minimum dwell is set in the Deck Builder and keeps working either way.
Four things behave in ways a deck's authoring surface does not warn about.
- Finishing the deck does not finish the segment. The last slide hands over to the segment's own Continue, and under transition mode auto with early advance off and no duration set, that button is never drawn — leaving the participant on the final slide with nothing to click, however many slides they got through. A last slide whose video has Advance when the clip ends ticked is the one way out of that combination, and it only works once the clip has actually played to the end. Otherwise: give an auto segment a duration, or allow early advance, or use a manual transition. The configuration panel does warn when an auto segment has no duration, which is the common way into this.
- Saving from the Deck Builder is what converts a legacy
slide. Until then the old
content/contentType/mediaUrlfields are what runs, adapted into a single slide. Once the segment has been saved from the modal it has a real deck, and those fields are dead weight — they stay on disk, but editing them through the API or a config import changes nothing on screen. - Interpolation is frozen at entry, for the whole deck.
{{var.X}}tokens in every slide are resolved once, when the participant reaches the segment — not when each slide is reached. A variable that changes while the participant is working through the deck does not update the later slides, so a long deck showing a live value will quietly show a stale one. - Changing the segment's Type discards the deck. Switching a slide segment to another type replaces its configuration with that type's defaults, without a confirmation, and every slide and block is gone.
The researcher drags Slide onto the chamber timeline, writes the slide's content, drops a {{var.condition}} token into it from the variable picker, and sets the transition so the segment ends on a participant click. Recorded before the Deck Builder existed, so the content and the variable picker are shown in the configuration panel rather than inside a text block's editor; the timeline and transition steps are unchanged.
§2 · Segments
2.7 The instruction segment
An instruction segment gives the participant one block of formatted
prose and one button. The text is authored as Markdown and rendered on screen
— headings, lists, emphasis, links, inline code, tables and rules all survive
— in a column 800 px wide by default, centred horizontally, and centred
vertically when the text is short enough to fit without scrolling. Beneath it sits a
single primary button, labelled “Continue” unless renamed. That button is
drawn disabled and stays disabled until the segment’s pacing
conditions are satisfied; when a button delay is set, the remaining seconds count
down inside the label itself — “Continue (5s)”, “Continue
(4s)” — so the wait reads as deliberate rather than as a broken page.
This is the segment to reach for whenever the participant must read something
and acknowledge it: the study brief, a consent-style preamble, the task
instructions before an interactive segment, or the framing text that differs between
conditions. What separates it from slide is not the content but the
shape: an instruction is one screen, and the segment is over when it is
acknowledged, whereas a slide segment is a deck the participant walks through
(§2.6). One prose screen with a button is
an instruction; a sequence is a slide segment. The instruction also owns the
acknowledgement itself — a renameable button, a delay before it becomes
clickable — so the click can be made to cost the participant a measured
number of seconds rather than being a reflex. Because the body is interpolated per participant before
it is shown, one instruction segment can carry condition-specific text through
{{var.X}} tokens instead of being duplicated once per chamberline
(see §4.5). Interpolation is resolved when
the participant enters the segment and is not refreshed afterwards, so a variable
that changes mid-segment does not alter text already on screen.
{{var.first_name}} has resolved for this participant.
| Parameter | What it controls |
|---|---|
Instruction Content (Markdown) | The body text. The configuration panel shows a rendered preview of the current content (or “No content yet”) plus an Edit content button that opens a full-screen editor: a formatting toolbar (bold, italic, heading, link, bullet list, numbered list, quote, inline code, image, table, horizontal rule), an Insert variable picker, and side-by-side editing and preview panes that collapse to Edit / Preview tabs on a narrow screen. Changes are committed only on Save; Cancel discards them. |
Content Alignment | Center (the default) or Left. This positions the whole text column within the screen; it does not change the alignment of text inside the paragraphs. |
Content Width | Narrow (800 px, the default), Medium (1000 px) or Wide (full width). The same width governs the row holding the button, so the button stays aligned with the prose above it. |
Show continue button · Button Text | Whether the acknowledgement button is rendered at all, and what it says. Shown by default, labelled “Continue”. The label is also the base of the countdown text while a button delay runs. |
Button Delay (seconds) | A fixed wait, counted from the moment the segment appears, before the button becomes clickable. Entered in seconds. This is the control that turns an instruction into enforced reading time, and it is the only pacing control that remains visible when the segment is embedded. |
Require scroll to bottom before proceeding | Intended to hold the button until the participant has scrolled the text to its end. It does not currently do so — see the caveat below. |
Minimum duration (Timing & Transition) | Shared with every segment, but treated specially here: the button is enabled only once the button delay has elapsed and the scroll requirement is satisfied and the minimum duration has passed. While the floor runs, a hint reading “Available in 0:42” appears beside the button. |
The instruction segment runs standalone by default, filling the screen, and can also be set to embedded so that it appears over a live chat. Embedded, it defaults to the inline layout — a card in the message stream — and can instead use split, where the prose fills the main area and the chat docks to a side panel; the latter suits a briefing that participants are meant to consult while they talk. It is one of the segment types whose interactive behaviour is rewired for the embedded path rather than simply drawn there, so the button delay and the disabled-button logic keep working inside the card. The usual embedding rules apply: the segment must sit immediately after a chat segment in the timeline to become its child, and on viewports narrower than 900 px the runtime falls back to standalone rendering.
Three parameters claim to pace an instruction segment. Only two of them do, and one combination can leave a participant with no way forward at all.
Require scroll to bottom before proceedingdoes not gate. The check is attached to the text element, but that element is not the one that scrolls — the surrounding container carries the overflow — so the initial check reports “already at the bottom” and releases the button on the first paint, however long the instruction is. Nothing warns you in the builder. UseButton Delay (seconds)or a minimum duration to buy reading time, and do not treat the checkbox as evidence that participants reached the end of the text.- Turning off
Show continue buttonremoves the only exit. The instruction segment renders no navigation of its own beyond that button, and the surrounding segment shell adds none. With the button hidden, the segment must be able to end by itself: give it a Duration with transition mode Auto, or hand control to the experimenter with Host. A hidden button plus a Manual or Sync transition and no duration strands every participant on the page. - Delay and minimum duration do not stack. Both clocks start when the participant enters the segment, so a 30-second minimum duration beside a 30-second button delay produces a 30-second wait, not sixty. Neither is redundant, though: the minimum duration also governs the automatic and synchronised advance paths, while the delay governs only the button.
- Embedding hides the timing controls. Choosing Embedded replaces the Timing & Transition card with the embedded start, stop and completion controls. A minimum duration set before the switch is still enforced but is no longer visible or editable, and the embedded card draws no “Available in…” hint, so the participant sees a disabled button with no explanation. Set the pacing with the button delay instead once a segment is embedded.
The researcher drags Instruction from the Library onto the chamber timeline, opens the Markdown editor with Edit content, writes a short brief using the toolbar and drops a {{var.condition_label}} token in from Insert variable while the preview pane keeps pace, saves, and then sets Button Text to “I have read this” and Button Delay to 5 seconds so the acknowledgement cannot be a reflex.
§2 · Segments
2.8 The media segment
A media segment gives the participant one thing: a full-width player
filling the content area — a video, an audio bar, or an embedded YouTube or
Vimeo frame — with the shared Continue button beneath it. As with
every segment, that button disappears when the transition mode is auto
and early advance is off, in which case the clip is the whole screen and the
segment ends on its own terms.
It exists so that a stimulus clip can be a step in the timeline in its own right, rather than something bolted onto a slide: a news vignette shown before a deliberation, an audio prompt each participant hears alone, a recorded briefing that must precede a task, or a debrief video at the end of a chamber. Because it is a distinct segment, it gets its own timing, its own transition rule, and its own row in the timeline — so “how long were they exposed to the stimulus” is a property of the design rather than something inferred from slide dwell time. The segment records no response of its own; it is exposure, not measurement, and the measurement belongs in whatever segment follows it.
| Parameter | What it controls |
|---|---|
Media Type | Video (the default), Audio, YouTube or Vimeo. Video and Audio build a native player around the URL; YouTube and Vimeo build a bare iframe at least 400 px tall. The two embed types are a different code path, not a skin — see the boxes below. |
Media URL | The address the player loads. Unlike the pdf segment there is no upload control and no Carrier-side hosting: the file must already be reachable over http(s), from a lab web server, an institutional store, or a CDN. The value is used as given, so a mistyped or expired URL shows an empty player rather than an error. |
Poster Image URL | A still frame shown before playback begins. Video only in practice — an audio element ignores a poster, and the YouTube/Vimeo path drops it. |
Autoplay | On by default. Asks the browser to start the clip as soon as the segment renders. Browsers routinely refuse; see the first box. |
Loop playback | Off by default. Restarts the clip when it reaches the end — useful for a short ambient stimulus, and incompatible with ending the segment when the clip ends. |
Show player controls | On by default. Renders the browser's own transport bar: play, pause, seek, volume. This is the participant's only means of controlling playback. |
Sync playback across participants | Nothing. The checkbox is checked by default and is read nowhere in the runtime; playback is per-participant and unsynchronised. |
Allow participants to pause | Nothing. Pausing is governed entirely by Show player controls. |
Captions URL | A WebVTT file attached as a default captions track. Standalone video only: the audio path, the YouTube/Vimeo path and the embedded renderer all omit the track. A cross-origin VTT additionally needs CORS headers on its host. |
Display Mode | Standalone (replaces chat) or Embedded (overlay on chat), with the usual Inline / Split choice when embedded — see below. |
Transition → Mode (shared field, special meaning) | For a self-hosted video or audio clip, setting the mode to auto is what arms “advance when the clip finishes”: the runtime listens for the player's end event and moves to the next segment, waiting out the minimum duration first if one is set. There is no separate checkbox for this behaviour, and the slide segment's “auto-advance when media ends” option has no effect on a media segment. |
The segment can run standalone or embedded. Standalone is the default; embedded
defaults to the inline card, and the split layout — clip in the content
pane, live chat docked alongside — is the natural shape for
“watch this while you discuss it”. The embedded form obeys the same
gating as every other embeddable type: the segment must directly follow the chat
segment it overlays, it takes the shared embedded start, stop and completion
parameters, and on viewports narrower than 900 px it falls back to
standalone rendering. Because media collects no response, the Data
Sharing, results-display and anonymity controls that appear on the interactive
types are absent here.
Four of the checkboxes on this panel read like guarantees and are not. Two of them are checked by default, which makes them worth knowing before a pilot rather than after:
Sync playback across participantsis inert. It is checked by default, but nothing in the runtime reads it: there is no synchronisation event and no shared clock. Every participant's clip starts when their own segment renders and runs at their own pace. If your design needs the group to see the same frame at the same moment, you must construct that yourself — a fixed segment duration, a preceding timer, and a clip short enough that drift does not matter.Allow participants to pauseis inert. Whether a participant can pause is decided byShow player controlsalone. Leaving this box unticked does not prevent pausing, and ticking it does not enable it.Autoplayis a request, not an instruction. Browsers block autoplay of audible media without a prior user gesture, and Carrier does not add amutedattribute to work around it — so the clip will usually sit still until the participant presses play. Precede the media segment with aninstructionsegment whose Continue click supplies the gesture, and leaveShow player controlson so there is a play button to press. Turning controls off while autoplay is blocked leaves the participant with no way to start the clip at all.Poster Image URLandCaptions URLapply to the standalone video path only. Audio, the YouTube/Vimeo embeds, and any embedded (inline or split) rendering silently drop them. An accessibility commitment made through the captions field therefore does not survive a switch to an embedded display mode.
End-of-clip auto-advance is the one genuinely media-specific behaviour in this segment, and it is invisible in the config panel. It requires a self-hosted video or audio clip and transition mode auto. Three configurations quietly lose it, and each needs a duration or a Continue button as a fallback:
- YouTube and Vimeo. The embed is a plain iframe; Carrier receives no end event from it. An auto-transition YouTube segment with no duration set therefore never advances, and with no Continue button rendered the participant is stranded. Give every embed segment an explicit duration.
- Looping clips. A looping clip never reaches its end, so it never fires the event. Loop and end-of-clip advance are mutually exclusive by construction.
- Embedded display. The embedded renderer does not attach the end listener — an embedded clip closes only on its embedded stop rule, its duration, or the card's Continue button, however it is configured.
One further caution on the embed types: because YouTube and Vimeo URLs are
passed to the frame untransformed, only an already-embeddable address
(https://www.youtube.com/embed/<id>) works. The ordinary
watch?v= link copied from the address bar is refused by the frame
and renders blank — a failure that looks exactly like a broken network
on the participant's screen, so it is worth checking in a pilot run rather
than in the builder preview.
The researcher drags Media onto the chamber timeline, leaves Media Type on Video, pastes the clip's URL, then sets the transition mode to Auto and clears the duration — arming the segment to end the moment playback finishes — while the inert Sync playback across participants checkbox sits ticked above it.
§2 · Segments
2.9 The PDF segment
A pdf segment puts a document on the participant's screen inside a
plain frame drawn by the browser's own PDF viewer — so scrolling,
zooming, page navigation, search and print are the browser's controls rather than
Carrier's, and behave exactly as the participant already expects them to. Run
standalone, the frame fills the segment area beneath the usual Carrier chrome
(progress bar, countdown, participant list) with a Continue button under
it. Run in its default split layout, the document takes the main pane and
the live chat is relocated into a docked column beside it, so participants can
argue about a text that stays in front of them. Run inline, it arrives as a card
in the message stream with a document header and a minimise chevron.
Its purpose is to carry the stimulus — a vignette, a news article, a policy
brief, a scientific paper, an information sheet — when the material is
longer or more typographically demanding than a slide can carry
honestly. It is a reading surface, not a response surface: a pdf
segment records no response of its own. That is why the Data
Sharing controls other segment types offer are absent from its panel — there
is nothing to share — and why the only trace it leaves in the export is how
long the participant had it open. Evidence that the document was actually
read has to come from its neighbours: a minimum-duration floor on the
segment itself, or a comprehension question in the segment that follows it.
| Parameter | What it controls |
|---|---|
| PDF URL | The document source, used verbatim as the frame's address. Only http: and https: addresses are accepted; anything else renders the words “Invalid PDF URL.” to the participant, and an empty field renders “No PDF URL configured.” |
| Upload PDF | Sends a local file to Carrier and, on success, overwrites PDF URL with the returned same-origin path /uploads/pdfs/<uuid>.pdf. The experiment must be saved before uploading; the cap is 25 MB; the file's first bytes are checked, so a renamed non-PDF is refused. The stored name is a fresh random identifier, so the hosted address cannot be guessed from the original filename. |
| Display Mode | Standalone (replaces chat) or Embedded (overlay on chat). Every control below this row appears only while the mode is Embedded. |
| Embedded Layout | Inline (card in chat) or Split (content + side chat). The choice re-draws the rest of the panel, because the sizing controls differ between the two. |
| Start Trigger | When the document appears during the parent chat: immediate, after N seconds, after N chat messages, or after the previous embedded sibling ends (with an optional delay in seconds; 0 fires at once). |
| Stop Trigger | When it closes: on participant action — which, for a document with nothing to submit, means the Continue button — or a hard auto-dismiss after N seconds. |
| Completion Behavior | What happens once it finishes: dismiss, end chat segment (the way a read-then-stop flow is built), lock (stays on screen but inert), or minimize to a pill. |
| Show Continue button on the card | Whether the participant may dismiss the document themselves. Clearing it is what produces a persistent reference panel that stays beside the chat until the stop trigger fires or the parent chat ends. This checkbox exists because the Timing and Transition cards are hidden for embedded segments; it is the only way to reach that behaviour. |
| Content pane width | Split layout only. The document pane's share of the row, 30–90% in steps of 5 (default 75); the docked chat column takes the remainder. |
| Card width · Card height | Inline layout only. Width as a percentage of the chat area (40–100%, default 75) and height as a percentage of the visible chat viewport (20–90%, default 35). The 35% default is a very short window for a document — raise it substantially, or use the split layout instead. |
| Pin to top while active | Inline layout only. Keeps the card fixed at the top of the chat while the segment runs, so it stays in view as messages scroll underneath; it unpins on completion. |
| Timing · Transition | Treated specially. A new standalone PDF segment is created user-paced — no duration, no warning time, transition mode manual — so participants read at their own speed and click Continue. When the segment is embedded, both cards disappear from the inspector entirely and are replaced by the Start / Stop trigger controls above. |
The pdf type is embeddable, and is the only segment
type whose factory defaults are embedded rather than standalone, and split rather
than inline — the split-screen reader is treated as the normal way to use
it, and the standalone document as the special case. Two gates apply. The split
layout needs a real chat parent: the runtime looks for a live chat segment to
dock, and if the PDF is not running under one it logs a warning and quietly falls
back to the inline card. And embedding of any kind is abandoned below a
900 px viewport, where every embedded segment reverts to standalone
rendering, so a participant on a phone reads the document full-width and never
sees the chat beside it.
Nothing in this segment reports its own failure to the researcher. Three of its four common problems are invisible in the builder and only surface in a pilot run — or, worse, in the data:
- A linked document may simply not appear. Most publishers, journals and news sites forbid being framed by another site, and a forbidden document renders as an empty frame with no error message — the participant sees blank space where the stimulus should be. The builder shows no preview of the participant frame, so this is undetectable until someone runs the segment. Upload the file instead of linking to it wherever the licence allows: a file Carrier hosts is served from Carrier's own origin and always displays.
- The embedded/split defaults only arrive by one route. They are applied when the type is set from the segment inspector's Type dropdown. Dragging the PDF tile in from the Library seeds a generic configuration with no display mode at all, so a dragged PDF segment is standalone — and if it is then switched to Embedded, the layout it is given is inline, not split. Whichever route you take, confirm Display Mode and Embedded Layout read what you intended before saving.
- Uploads fail on an unsaved experiment. The upload is attached to an experiment record, so a brand-new experiment must be saved once before the file picker will work; until then it answers “Please save the experiment before uploading a PDF.”
- The segment stores nothing. There is no submission, no per-page record, no scroll or dwell trace inside the document — only the segment's start and end times. A PDF segment on its own cannot tell you whether a participant read the stimulus or clicked straight past it, so pair it with a minimum duration, a following comprehension item, or both.
The researcher adds a pdf segment after a chat segment, uploads a local document so Carrier hosts it and the PDF URL field fills with the hosted path, sets Display Mode to “Embedded (overlay on chat)” and Embedded Layout to “Split (content + side chat)”, drags the content pane to 70%, and clears Show Continue button on the card so the document stays open beside the conversation until the chat itself ends.
§2 · Segments
2.10 The timer segment
A standalone timer segment clears the activity area and leaves one
centred card on screen: a line of text — “Please wait...” unless
the researcher changes it — a large minutes-and-seconds readout that ticks
down once a second, and a horizontal bar that fills as the wait elapses. The usual
segment shell remains around it: the progress indicator, the participant list and
the chamber clock, subject to the chamber's own visibility settings. What is not
there is a Continue button. The timer renderer emits no navigation
controls at all, so there is nothing for a participant to press; they wait, and the
segment ends itself. If they reload the page or drop and reconnect, the remaining
time is recomputed from the server's record of when the segment started, so a
refresh does not restart the wait.
A timer is a pacing instrument rather than a measurement one. It records nothing,
accepts no input, and gives non-human participants nothing to do. Its uses are all
variants of buying time on purpose: a short settle-in pause after matching, before
a discussion begins; an enforced deliberation delay between reading a case and
deciding on it; a synchronising beat while slower members of the chamber finish an
earlier activity; an inter-trial interval between repeated measures. Two nearby
instruments are often the better choice. If the requirement is
“spend at least thirty seconds on this page”, a Min Duration on
the page's own segment enforces that floor without also imposing a ceiling
(§2.5). If the wait exists because the
server is computing something, a process segment ends when the work
finishes rather than when a clock runs out.
The type configuration panel offers six controls. Two of them reach the participant; the other four are documented here because they are visible in the builder, not because they do anything.
| Parameter | What it controls |
|---|---|
| Display Message | The text above the countdown; defaults to “Please wait...”. It is inserted as trusted author markup, so HTML tags written here are rendered rather than shown literally. Unlike slide and instruction content, it is not variable-interpolated — a {{var.name}} token would appear on screen as typed. |
| Show countdown | On by default. Draws the ticking readout and the progress bar; unchecked, the card shows the message alone. It is a display switch only: the segment still expires at the same moment either way, and the chamber clock in the shell is a separate control. |
| Countdown Format | Offers Digital (00:00), Circular progress and Progress bar. Inert — config.countdownFormat is never read, and every participant sees the digital readout with a bar beneath it whichever option is stored. |
| End Message | Intended as the text shown once the countdown reaches zero. Inert — nothing reads it; the Display Message stays on screen until the segment advances. |
| Show “waiting for others” indicators | Inert. Whether the participant list appears is decided by the chamber's own setting together with the per-segment participant-list toggle, not by this box. |
| Allow participants to end early | Inert, and the most misleading of the four: it reads as permission to skip the wait. Early advance is governed by Allow early advance in the Transition panel — and on a standalone timer even that renders no button, because the timer emits no navigation. |
| Max Duration (Timing panel) — shared field, load-bearing here | The wait itself, and the only thing that ends a standalone timer. Expiry advances the segment regardless of transition mode. Left empty the clock reads ∞ and the segment never ends. |
2.10.1 Standalone and embedded
The timer is one of the embeddable types: its Display & Sharing card offers Standalone and Embedded (overlay on chat), and the default embedded layout is inline — a card in the message stream — with split available. It carries no gating beyond the general rules for embedded children: the segment must sit immediately after the chat it belongs to, and below 900 px of viewport width the runtime renders it standalone instead. The card is cosmetic, though. The embedded path draws the message and a number, but never starts the per-second countdown, so the figure on the card is a static one that does not move; and because choosing Embedded hides the whole Timing panel, the duration it is drawn from can no longer be edited in the inspector. An embedded pause should therefore take its timing from the embedded stop trigger (“auto-dismiss after N seconds”) and be read as a notice rather than a clock.
The timer's configuration panel promises more than the segment delivers. Four of its controls — Countdown Format, End Message, Show “waiting for others” indicators and Allow participants to end early — are stored in the experiment but read by nothing at run time. Three consequences are worth planning around:
- A standalone timer has no advance control of any kind. It renders neither a Continue button nor a sync-readiness button, so neither Allow participants to end early nor Allow early advance can give participants a way out. The segment ends when Max Duration expires and at no other moment.
- A timer with no Max Duration strands the participant. The clock shows ∞, nothing expires and there is no button; the only remaining remedy is to end that participant's session from the dashboard (§6.3). Always set a duration before piloting.
- A timer dragged in from the segment library arrives at five minutes with its transition mode set to Sync, inherited from the generic segment defaults rather than chosen for this type. Sync's readiness gate is unreachable here for the reason above, so set Max Duration deliberately and set the transition mode to Auto, which is what actually describes the behaviour.
The researcher drags a Timer segment into a chamber's timeline, sets its Display Message to “Take a moment to think before the discussion begins.”, replaces the inherited five-minute Max Duration with 20 seconds, and changes the transition mode from Sync to Auto — and the timeline card updates to show a 20-second auto-advancing wait.
§2 · Segments
2.11 The survey segment
A survey segment hands the main area over to a Survey.js form. The
questionnaire arrives with its own chrome — pagination, Next and Previous
buttons, required-field markers, inline validation messages and a Complete button
— rendered inside the usual segment frame, with the progress indicator,
countdown clock and participant list showing or hidden according to the chamber’s
settings. Standalone, Carrier adds no advance button of its own: the Survey.js
Complete button is the only way forward, and pressing it saves the answers and moves
the timeline on in one motion.
It is the same survey engine that renders the experiment-level and chamber-level surveys, so the segment is not the place for a long battery that could simply be attached to the chamber. What the segment adds is position and consequence. Position, because it occupies an exact slot in the chamber’s inner timeline — a manipulation check wedged between two chat segments, a mood measure taken immediately before a deliberation rather than fifteen minutes earlier, a confidence rating collected the moment a task ends. Consequence, because it is the only survey surface that can end or divert the run on the strength of an answer, which makes it the natural home for screeners and exclusion criteria as well as for measures. Each answer is stored under a researcher-chosen key, so a survey segment is also the usual way of manufacturing a variable mid-run for later matching (§4.3) or prompt interpolation (§4.5).
| Parameter | What it controls |
|---|---|
Survey Definition |
The questionnaire itself. A visual question builder is mounted over the stored Survey.js JSON, with a two-button toggle in its header — Visual builder and Raw JSON — a draggable question list, an Add Question button and a link out to the SurveyJS Creator. Questions composed visually are written back as a single-page definition. |
Type (per question) |
One of seven answer formats: Short Text, Long Text, Single Choice, Multiple Choice, Dropdown, Rating, Yes / No. This is a deliberate subset of Survey.js — matrices, panels, file upload and conditional question visibility exist in the underlying library but have no control in the visual builder, and are reachable only through Raw JSON or the SurveyJS Creator. |
Question Title |
The wording the participant reads. The form treats it as mandatory: Add / Update refuses to save while it is empty. |
Variable Name |
The key the answer is stored and referenced under — in the export,
in chamberline assignment (§1.2),
in matching constraints, in prompt interpolation, and in the
Completion Conditions below. Left blank it is generated as
q<timestamp>, which is unreadable in the data and
unusable in every rule that would want to name it. The chosen key is
echoed on the question card as a var: badge. |
Required |
Marks the question isRequired, so Survey.js refuses to
complete until it is answered. Once the question form is closed this is
visible only as a Required badge on the question card. |
Choices (one per line) · Min /
Max |
Type-conditional answer options, shown and hidden as the
Type dropdown changes: a one-option-per-line textarea for
Single Choice, Multiple Choice and Dropdown; a numeric range for Rating,
defaulting to 1–5 and bounded at 0–10, so a 0–100 scale
is not reachable from this form. |
Anonymous responses |
A checkbox stored on the segment. Nothing in the runtime, the export or the dashboard consults it — see the caution below. |
Completion Conditions |
A collapsed accordion beneath the type card, offered for
survey and attention-check segments only. Each
rule pairs an If field — a question’s
Variable Name, matched exactly — with an operator
(=, ≠, in, >,
<) and a value, then a Message and an optional
Redirect URL. The in operator reads its value as a
comma-separated list. Rules are evaluated on completion, before the
normal advance; the first match saves the submission, then ends the run
with that message and redirect. This is how a screener excludes someone
mid-study. |
Data Sharing (and Results Display,
Anonymous Results) |
Offered because the survey counts as an interactive type, but no results view is implemented for it — see the caution below. |
Display Mode and its embedded controls |
Standalone or embedded. Choosing embedded reveals
Start Trigger and Stop Trigger,
Completion Behavior, Embedded Layout
(inline or split), Show Continue button on the card, and the
sizing controls for the chosen layout — Card width,
Card height and Pin to top while active for
inline, Content pane width for split. |
Min Duration (Timing) |
Implemented differently here than anywhere else, because the segment has no advance button of its own to disable. Carrier instead takes over the Survey.js Complete button: it is disabled and its label rewritten to a live countdown — “Continue in 0:42” — reverting to the survey’s own completion text when the time expires. The button is re-found and re-disabled on every tick, since Survey.js rebuilds its navigation on each page change, and completion is refused outright until the minimum has elapsed in case a re-render lets a click through. |
Max Duration and Transition Mode |
A new survey segment is created user-paced — no duration, no
warning time, transition mode manual — but because the
standalone renderer emits no navigation, that manual mode produces no
visible Next button and advancement rests entirely on Survey.js. If a
maximum duration is set and expires mid-form, the runtime blurs the
focused field first (Survey.js text inputs only commit on blur), flushes
the partial answers, and tags the saved record timedOut so
abandoned forms are distinguishable from real submissions in the export. |
The survey is embeddable, but defaults to standalone. Embedded, it mounts the same Survey.js model into a card inside the chat stream — inline by default, at 75% of the chat width and 35% of its height, which is cramped for anything longer than a question or two. The split layout gives the form the main area with the live chat docked beside it, but it needs a chat segment to dock into: mounted anywhere else it silently falls back to the inline card. Embedding as a whole is gated on viewport width — below 900 px the segment renders standalone regardless of the configured mode, so a mid-conversation manipulation check becomes a full-screen one on a phone. General embedding behaviour, start and stop triggers and completion behaviour are covered in §2.
The survey segment carries two inert controls, one behaviour that does not survive the move into a chat, and one lossy editor. None of them fails loudly, so each is worth checking before a study goes out:
Anonymous responsesanonymises nothing. The checkbox writes a flag that no runtime, export or dashboard code reads; responses stay attributed to the participant exactly as they would be with it unticked. Anonymity has to come from the study design, not this box.Data Sharingis inert for this type. The dropdown renders because the survey is classed as interactive alongside selection, ranking, input, task and social, but the code path that shows other participants’ submissions has no case for a survey and there is no survey results view to show. Setting it to Real-time or After all submit changes nothing a participant sees.Completion Conditionsare evaluated only on the standalone path. The embedded renderer builds its own completion handler, which advances on the minimum-duration gate alone and never consults the rules. A screener written into an embedded survey card will therefore never fire — keep exclusion rules on a standalone survey (or on anattention-check, which shares the same panel).- One further edge, on the definition rather than the run: the visual builder flattens every page’s elements into a single list, and rewrites the definition as one page when a question is added or edited. A multi-page questionnaire pasted through Raw JSON keeps its page breaks only until the first visual edit, and anything the parser cannot read — matrices, panels, conditional visibility — comes back as an empty question list rather than an error.
The researcher drags a Survey segment onto the chamber, adds a Single Choice question in the visual builder titled “Have you taken part in a study like this before?” with the variable name prior_exposure and the choices Yes and No, then opens the collapsed Completion Conditions accordion and writes one rule — if prior_exposure = Yes, end the session with a thank-you message and a redirect — and the panel settles with the question card and its exclusion criterion side by side.
§2 · Segments
2.12 The input segment
A participant meets an input segment as one question and one field to
answer it in: a four-row textarea, a single-line text box, or a number field,
carrying whatever placeholder text the researcher supplied. A hint line may sit
underneath — “Value range: min: 0, max: 100” for a numeric field
— and below that sits the ordinary Continue button. That button is
live-gated: it enables and disables on every keystroke as the answer starts
and stops satisfying the segment’s validation rules. It does so silently.
There is no error message, no highlight on the field, and no explanation of what is
wrong — only a button that will not respond.
The segment exists for the case where there is exactly one thing to ask: an
open-ended justification for a choice just made, a one-line explanation, a numeric
estimate, a willingness-to-pay figure. It is the lightweight alternative to a
survey segment — one prompt, one answer, no form schema to author
— and its answer is a first-class segment submission, readable by later
segments and by trigger conditions as a variable
(§4.2) and carried into the export as the
segment’s recorded data (§6.5). Note
that non-human participants can submit into an input segment as well as
humans (§5.5), which the catalogue table in
§2 and Appendix C both fail to record.
2.12.1 Parameters
| Parameter | What it controls |
|---|---|
Prompt |
The question shown above the field. A plain single-line text box: the
text is injected into the participant’s page unescaped, so raw
HTML passes through, but Markdown is not rendered and
{{var.X}} tokens are not interpolated. Unlike the
slide and instruction editors, this field has
no variable picker. |
Input Type |
Single line, Multi-line (the default), or Number. Chooses the field
that is rendered and rebuilds the panel below to show either the length
fields or the value fields. One asymmetry to know about: the builder
writes textarea by default, but the runtime falls back to
a single line when the key is absent altogether, so a hand-authored or
imported configuration that omits it renders as a one-line box. |
Placeholder |
Grey hint text inside the empty field. Injected unescaped into the HTML attribute, so a quotation mark inside it breaks the markup. |
Min Length / Max Length |
Nominally a character floor and a character cap for text answers; hidden when Input Type is Number. Both are inert — see the warning below. |
Min Value / Max Value |
Lower and upper bounds for a numeric answer, each blank meaning no
bound. Shown only when Input Type is Number, and genuinely enforced:
they set the field’s min and max
attributes, gate the Continue button, and produce the “Value
range” hint beneath the field. |
Validation Pattern (regex) |
A regular expression the answer must match before Continue enables.
Offered for all three input types and genuinely enforced, recompiled
and tested on every keystroke. It is currently the only working way to
require a minimum length: .{50,} in place of a Min Length
of 50. |
Validation Error Message |
The message intended to appear when validation fails. Inert — nothing in the participant runtime or the server reads it. |
Anonymous input |
A legacy per-segment anonymity flag, retained only as a fallback for Anonymous Results when resolving the name beside a shared answer. It does nothing on its own; new configurations should use Anonymous Results. |
Data Sharing |
Whether the other occupants of the chamber see this answer: Never (the default), after all have submitted, or in real time. Real time deserves a moment’s thought here, because the field autosaves: typing emits a submission after every 500 ms of quiet, so real-time sharing broadcasts half-finished sentences as they are composed rather than a considered final answer. |
Results Display / Anonymous Results |
Revealed once Data Sharing is not Never. Anonymous Results works, replacing names with “Participant 1”, “Participant 2” and so on. Results Display does not: whichever of Aggregated or Individual is chosen, free text is always listed one entry per participant, because there is no meaningful aggregate of open-ended prose. |
Timing & Transition |
Treated specially for this type. input is one of the
user-paced segment types, so a new one is created with no duration, no
warning time and a manual transition — it waits for
the participant rather than the clock. Minimum duration is the one
timing field that reaches the validation logic: it is ANDed into every
enable/disable decision, so Continue stays disabled until the floor has
elapsed even for an answer that is already valid. |
2.12.2 Standalone and embedded
input runs either standalone, replacing the chat view for the duration
of the segment, or embedded as a card laid over a live chat. The embedded renderer
implements it properly — the validation, the button gating and the debounced
autosave all keep working inside the card — and an embedded input is treated
as a child of the nearest preceding chat segment. The default is standalone; when
Embedded is chosen, the Timing and Transition cards disappear in favour of a start
trigger and a stop trigger, and the default layout is inline (a card in
the message stream) rather than split (a content pane beside a docked
chat column). Two cautions are specific to this type: the inline card’s
default height of 35 per cent of the chat viewport is tight once a prompt, a four-row
textarea and a hint line are stacked inside it, and a time-based stop trigger will
dismiss the card mid-sentence, discarding whatever had not yet autosaved. Below
900 px of viewport width, and for mediator and observer views, an embedded
input falls back to standalone rendering.
A participant whose answer fails validation sees no message and no highlight — only a Continue button that stays greyed out. That makes the three gaps below more consequential than they look in the panel, because none of them announces itself at run time:
- Min Length and Max Length do nothing. The panel writes
them to
config.minLengthandconfig.maxLength, while the runtime’s validator readsvalidation.minLengthandvalidation.maxLength— keys the builder never writes. A Min Length of 50 therefore neither gates the button nor draws the “Minimum 50 characters” hint that the runtime is capable of showing. Express a length floor as a Validation Pattern instead (.{50,}). Thetasksegment’s identically-labelled fields are wired up correctly, which makes the difference easy to miss. - Validation Error Message is never displayed. Any rule enforced by a Validation Pattern must therefore be stated in the Prompt text as well — “at least two sentences”, “digits only” — or the participant is left guessing why the button will not move.
- A pattern that matches the empty string will not stop a blank
answer. The expression is tested against the field’s
current contents, empty included, so an unanchored or
*-quantified pattern enables Continue on an untouched field. Anchor it and require at least one character (+, or{n,}). Because the expression is recompiled on every keystroke, a malformed pattern also throws inside the typing handler rather than being reported anywhere in the builder.
The researcher adds an input segment, types the question into Prompt, sets a Placeholder and a Validation Pattern of .{50,} in place of the inert Min Length field, then switches Input Type to Number and the two length fields give way to Min Value and Max Value.
§2 · Segments
2.13 The selection segment
A selection segment puts a single question on the screen and asks every
participant in the chamber to answer it. The question is rendered from markdown at the
top of the segment area; beneath it sits either a list of options — radio buttons
in single mode, tick boxes in multiple mode, with a “Select 1-3 options”
hint under the list when more than one answer is allowed — or, in slider mode, a
track with an anchor label at each end and a large live readout of the current value.
The Continue button below starts disabled and only becomes clickable once the
number of ticked options falls inside the Min/Max range (and any minimum duration has
elapsed); clicking it before then flashes the option list red rather than advancing.
A slider is the exception: it always reports a value, so its Continue is live
from the first paint.
It is the segment to reach for whenever a design needs a discrete answer on the record:
a vote at the end of a deliberation, a forced choice between two framings, a manipulation
check, or — in slider mode — a Likert or visual-analogue rating. Its answers
are not merely logged. Every submission is readable downstream as
segment-submission:<segId>:selections,
segment-submission:<segId>:optionValues.0 or
segment-submission:<segId>:sliderValue, so a choice made here can
steer a later chamber, feed an aggregate, or interpolate into an instruction
(§4). It is also one of only two types
— with ranking — for which a scripted or LLM participant can be
configured to submit a confederate answer alongside the humans
(§5).
2.13.1 Parameters
| Parameter | What it controls |
|---|---|
| Question / Prompt | The question shown above the options. Rendered as markdown at run time, so **bold**, links and  all work; the Preview button beside the box shows the rendered result without leaving the builder. |
| Selection Mode | Single choice (radio buttons), Multiple choices (tick boxes) or Slider (continuous scale). The mode decides which block of fields below is shown, and which of the two answer shapes the segment records. |
| Options (one per line) | The choice list, one option per line. Appending | <value> to a line attaches a numeric or string value to that option, which is recorded as optionValues alongside the chosen indices — the way to make a five-point scale sum or average without a lookup table elsewhere. |
| Min Selections · Max Selections | The floor and ceiling on how many options may be ticked before Continue enables. Both default to 1. Hidden in slider mode. |
| Min Value · Max Value · Step Size · Default Value | Slider bounds, increment and starting position (0, 100, 1 and 50 by default). Step accepts fractions, so a 0–1 continuous scale is expressible. |
| Left Label · Right Label | Anchor text at each end of the track, e.g. “Strongly Disagree” and “Strongly Agree”. Left blank, the numeric bound is shown instead. |
| Show current value | Whether the live numeric readout appears above the slider. On by default; turning it off gives a true visual-analogue scale, where participants cannot anchor on the number. |
| Anonymous voting | A legacy anonymity flag, superseded by Anonymous Results. The runtime consults it only as a fallback when Anonymous Results is unset. |
| Allow changing vote | Nominally whether an answer may be revised. Nothing reads it: a participant can always change a selection until Continue is pressed. |
| Data Sharing | Never (the default), After all submit (results revealed once everyone in the chamber has answered) or Real-time (results update live as answers arrive). Option modes then draw a horizontal bar chart of the distribution; slider mode draws a responses/average/min/max row with an average marker on the track. |
| Results Display | Aggregated (distribution or averages) versus Individual (each participant's answer listed separately). Shown only when Data Sharing is not Never. |
| Anonymous Results | Replaces names with “Participant 1”, “Participant 2” in shared results. Shown only when Data Sharing is not Never, and the field the runtime actually reads. |
| Display Mode | Standalone (replaces the chat) or Embedded (a card overlaid on a running chat). Choosing Embedded removes the Timing and Transition cards from the panel altogether and adds a Visibility Conditions accordion, which shows the card only to participants matching the listed property conditions. |
| Start Trigger · Stop Trigger · Completion Behavior | Embedded only: when the card appears (immediately, after N seconds, after N chat messages, or a delay after the previous child ends), when it closes (on submission, or a hard auto-dismiss timeout), and what happens afterwards (dismiss, end the parent chat, lock, or minimise to a pill). |
| Embedded Layout · Card width · Card height · Pin to top while active · Content pane width | Embedded only: Inline renders a card in the message stream, sized as a percentage of the chat area and optionally pinned to the top so it stays in view as messages scroll; Split puts the poll in a content pane beside a docked chat column, sized by pane width alone. |
| Show Continue button on the card | Embedded only. It is a proxy for the transition settings the hidden Transition card would otherwise hold, and it is the only control that lets a participant close the card deliberately once they have answered — sensible to disable on a read-only reference panel, never on a poll. |
Timing and transition are shared with every other segment, but a new
selection is created user-paced rather than timed: no duration, no warning
time, and transition mode manual, so it waits for the participant instead of
running a clock. Setting a duration turns it into a timed poll; setting mode
sync holds the whole chamber until everyone has answered.
2.13.2 Standalone and embedded
Selection renders in both display modes, and embedding it is the canonical use of the
embedded mechanism: a poll that overlays a live conversation lets a group vote without
leaving the discussion, and a chat whose transition mode is embedded-complete
ends exactly when the vote is in. The default is Standalone, and the default
embedded layout is Inline. The gating is positional rather than a setting: an
embedded segment becomes the child of the nearest preceding chat segment
in the same chamber, so a segment that is not preceded by a chat has nothing to overlay.
Two run-time conditions force a fallback to standalone rendering regardless of the
configuration — a viewport narrower than 900 px, and any mediator or
dashboard observer, who sees the mediator view rather than the interactive widget.
Most of these are silent: the segment saves, renders, and collects data, but not the data the configuration appears to promise.
- Switching to Multiple choices does not raise Max Selections. It stays at its default of 1, so participants get tick boxes but Continue disables again the moment a second box is ticked — a multi-select poll that can only ever record one answer. Raise it explicitly whenever the mode changes.
- A submission is emitted on every click, not on Continue. Under Real-time sharing the rest of the chamber sees provisional answers as they are being weighed; under After all submit the reveal fires once each participant has clicked once, which may be well before anyone has settled. Downstream variables track the same provisional values and settle on the last click before the segment ends.
- A slider has no untouched state. The handle starts at Default Value and reports it immediately, so a participant who never touches the slider still records that number as an answer, indistinguishable from a deliberate one.
- The prompt does not interpolate variables.
{{var.X}}is substituted only ininstructionandslidecontent; a token written into a selection prompt reaches the participant as literal text. Put the variable in a preceding instruction segment instead. - Two settings are inert. Allow changing vote is read by nothing, and Anonymous voting is only a fallback for Anonymous Results. Anonymity should be set with Anonymous Results, which requires Data Sharing to be enabled to appear at all.
The researcher adds a selection segment to a chamber, writes the question, switches Selection Mode from “Single choice” to “Multiple choices”, types three options with per-option values, and raises Max Selections from 1 to 3 so that more than one box can actually be ticked.
§2 · Segments
2.14 The ranking segment
A ranking segment shows a prompt line and, beneath it, a vertical list
of the items to be ordered. Each row carries a grip handle on the left, its current
position number, and the item's label. Rows are reordered by dragging them past one
another; the position numbers renumber the moment a row is dropped, and every drop
records the new order. Below the list sits the standard Continue button. There is no
keyboard or button-based alternative to dragging at runtime — the up/down
arrows that appear in the builder's playtest preview are a preview affordance only
— so a ranking segment assumes a pointer.
Researchers reach for it in three situations: eliciting a preference order over a
fixed set of alternatives, asking a group to set priorities, and recording a
collective ordering at the end of a deliberation. The third is the common case, and
it is usually configured as an embedded card over a chat so the group can rank while
still talking. Like selection, a ranking's output is readable downstream
as a segment-submission variable (§4.2), and
a scripted or LLM participant can be configured to submit an ordering alongside the
humans (§5.5).
2.14.1 Parameters
| Parameter | What it controls |
|---|---|
Instruction | The prompt line above the list. A plain single-line text field — no markdown, no variable interpolation, and no Preview button. It is honoured only in embedded display; see the caveat below. |
Items (one per line) | The list to be ranked, one item per line. Appending | <value> attaches a numeric or string value to an item, and submissions then expose rankedValues in rank order — so segment-submission:<segId>:rankedValues.0 reads the value of whatever the participant put first. The typed line order is the order every participant is shown; nothing is shuffled. |
Ranking Mode | Nominally Individual versus Collaborative (a shared ranking). Inert — see the caveat below. |
Lock ranking after submit | Nominally freezes the order once submitted. On by default, and inert — see the caveat below. |
Data Sharing, Results Display, Anonymous Results | Whether participants see one another's orderings — Never (the default), After all submit, or Real-time — and, once sharing is on, whether the reveal is Aggregated (an “Average Rankings” list giving each item's mean rank, best first) or Individual (each participant's ordering listed in full), with names optionally replaced by “Participant 1”, “Participant 2” and so on. |
Display Mode | Standalone (replaces the chat view) or Embedded (an overlay on an active chat). Standalone is the default. Choosing Embedded hides the Timing and Transition cards and reveals the embedded controls below, plus the Visibility Conditions accordion. |
Start Trigger, Stop Trigger, Completion Behavior | Embedded only. When the card appears (immediately, after N seconds, after N chat messages, or chained after the previous embedded sibling), when it closes (on the participant's submission, or a hard timeout), and what happens afterwards — dismiss, end the parent chat, lock, or minimise to a pill. |
Embedded Layout | Embedded only. Inline (a card in the chat stream, the default) or Split (the list fills the main pane with the live chat docked beside it). Split is worth preferring for ranking specifically: a list of more than a handful of items is awkward to drag inside a short inline card. |
Card width, Card height, Pin to top while active | Inline layout only. Card width as a percentage of the chat area (40–100%, default 75) and content height as a percentage of the chat viewport (20–90%, default 35), plus whether the card stays fixed at the top as messages scroll. The card's content area scrolls, so a long list stays reachable — but the drag target shrinks with it, which is the practical argument for raising the height beyond about five items. |
Content pane width | Split layout only. The content pane's share of the row, 30–90%; the docked chat column takes the remainder. |
Show Continue button on the card | Embedded only. Whether the card carries its own Continue button. It must stay on for a ranking card, because there is no other way for the participant to commit the order. |
| Timing and Transition | Ranking is a user-paced type: a new ranking segment is created with unlimited duration, no warning time, and a manual transition, rather than the timed defaults most segment types receive. A minDuration, if set, disables the Continue button until it elapses. Both cards are hidden when Display Mode is Embedded, where the start and stop triggers take over. |
2.14.2 Display modes
Ranking is one of the embeddable types. It defaults to standalone with an inline embedded layout, and the builder places no restriction on where an embedded ranking may sit — at runtime it is treated as a child of the nearest preceding chat segment in the chamber, so an embedded ranking with no chat before it has no parent to overlay. Two conditions force it back to standalone regardless of configuration: a viewport narrower than 900 px, and a viewer watching as a mediator or observer. Shared results appear beneath the list in either mode.
The ranking panel is older than most of its neighbours. Three of its fields never reach the runtime, and two absences in the runtime itself change what the recorded ordering means. All five are cheap to work around at design time and expensive to discover after data collection.
Instructionis dropped in standalone display. The standalone renderer reads only apromptfield and falls back to the generic “Drag to rank items:”; only the embedded renderer falls back toInstruction. A standalone ranking therefore shows the generic line no matter what is typed, and the wording that defines the task — “rank by importance” versus “rank by likelihood” — never reaches the participant. Until this is fixed, put the framing in aninstructionorslidesegment immediately before, or run the ranking embedded.Ranking ModeandLock ranking after submitare inert. Neither is read by the runtime or the server; both survive only in the builder's own prompt text. Selecting Collaborative does not produce a jointly edited list — every participant still ranks alone. The closest available approximation to a group ordering is Individual withData Sharingset to Real-time, so each participant ranks for themselves while watching the running average.- Nothing gates the Continue button. A ranking never blocks advance, so a participant who drags nothing still submits — and what is submitted is the item order exactly as it was typed into
Items. In the export an untouched list and a deliberate endorsement of the starting order are identical. Two consequences follow: the typed order is an anchor, not a neutral starting point, so vary it across conditions if the ordering itself is the measure; and to distinguish real responses from inaction, count only participants who produced at least one reorder event. - Aggregated results are strict about length. The “Average Rankings” view discards any submission whose ordering does not have exactly one entry per item, and renders nothing at all if no submission qualifies — so an empty results panel after a reveal means no usable submissions, not zero participants.
The researcher drags a Ranking segment onto the chamber timeline beneath a chat, types an instruction and three items carrying per-item values, then switches the display mode to “Embedded (overlay on chat)” and the embedded layout to “Split” — the Timing and Transition cards give way to the embedded start, stop and pane-width controls, and the timeline re-draws the ranking as an indented child of the chat above it.
§2 · Segments
2.15 The chat segment
A chat segment fills the participant's screen with one scrolling
message stream and, beneath it, a text box that grows as it is filled and a
paper-plane send button. Around that sits the usual segment shell — segment
progress, the countdown clock and the participant list in the left sidebar, unless
the chamber or this segment turns them off. Every other occupant of the chamber
writes into the same stream, and nothing in the interface distinguishes them by
kind: another human, an LLM chatbot, a scripted chatbot and an agent all produce
ordinary message bubbles, complete with typing indicators that appear and clear on
their own. There is one room, everyone is in it, and there is no channel to switch
to. Participants holding a non-communicator role see a different screen entirely
— a processor gets a read-only observation column beside its draft panel, a
mediator gets the broadcast console — described in
§3.1.
This is the segment used for deliberation, negotiation, dyadic advice-giving, interviewing, and any design where the dependent measure is what people say to one another. It is also the surface most of the trigger system watches: keyword, message-count, sequence and after-bot-message triggers (§5.1) are all evaluated against a chat segment's message stream, so a chamber's scripted behaviour usually lives or dies here. Finally, it is the host container for embedded child segments — the way a vote or a ranking can be put to a group mid-conversation without taking the conversation off screen.
2.15.1 Type configuration
| Parameter | What it controls |
|---|---|
| Communication Channel | Text, Audio, Video, or None (bots/agents only). In practice only None changes anything: it disables the composer, replaces its placeholder with “Chat is disabled in this segment”, and is enforced again on the server, which rejects human messages for that segment. Choosing Audio or Video here has no effect — media capture is initialised once per chatroom from the chamber's channel, not the segment's. |
| Max Messages | Intended as a per-segment message cap; help text reads “0 or empty = unlimited”. Recorded in the configuration and shown in the segment inspector, but never enforced at runtime (see the caveat below). The chamber-level message cap is a separate setting. |
| Show typing indicator Agent Think Delay Min / Max (ms) |
The checkbox is a disclosure control: unticking it hides the two indented delay fields, which are described in the builder as a legacy fallback for per-chatbot thinking delays. None of the three is read at runtime — typing indicators are always shown, and a non-human participant's pause before replying comes from its own thinking-delay setting. |
| Show message history from previous segments | Nominally carries the conversation forward from earlier segments of the same chamber. Not consulted by the participant client; what the stream shows is unaffected either way. |
| Show participant list | Live, and one of the few segment-level overrides of the shared shell. The sidebar list appears only when the chamber's own participant-list setting and this checkbox are both on; turning it off here can change the shell from a sidebar to a floating countdown pill. Useful for hiding group composition during a manipulation while leaving it visible elsewhere in the chamber. |
| Enable emoji reactions Enable message reporting |
Add, respectively, a reaction picker and a report flag to the hover action row on message bubbles. Reporting is deliberately not offered on a participant's own messages. The action row is drawn only if at least one of the two is on, so leaving both off keeps bubbles completely inert. |
| Enable behavior tracking | Records browser-level behaviour for the duration of the segment — tab switches, scrolling, mouse movement, typing patterns and focus changes — and writes it to the experiment export. This is one of the few per-event process-tracing streams Carrier produces, so it is worth turning on wherever attention or hesitation is of interest; it costs nothing visible to the participant. |
| Control Events | A chat-only list of rules that disable or re-enable the composer, or force the segment to end, when a condition is met in the conversation. Added through the + button; detailed in 2.15.2 below. |
| Transition Mode → Embedded complete | The shared Transition card gains a fifth mode for chat: the chat ends when all of its embedded children have finished. The option is offered only while the segment immediately after this chat is an embedded child — any non-embedded segment in between removes it. General transition behaviour is in §2.5. |
| Fallback timeout (seconds) | Appears under the transition mode only when that mode is Embedded complete. A safety net: if the embedded chain stalls — a message-count start trigger that never fires, a child hidden from every participant by a visibility condition — the chat advances anyway after this many seconds. Left empty, the chat waits indefinitely. |
2.15.2 Control events
Control events are the one part of the chat panel with no equivalent elsewhere in the builder, and the reason a chat can be made to close itself on the state of the conversation rather than on the clock. Each event pairs an action with a condition; the modal opened by the + button shows only the sections the chosen action needs.
| Parameter | What it controls |
|---|---|
| Action | Disable Input, Enable Input, or End Section (force advance). This choice governs the rest of the modal: the two input actions reveal Target and the optional agent-script block; End Section reveals Activation and the timeout pair. |
| Target Type Participant Name |
Input actions only. Either all human participants, or one named participant — the name list is drawn from the chamber's own participant slots, so an empty chamber offers nothing to select. |
| Activate On | End Section only: when the rule starts watching — at segment start, after a delay in milliseconds, when a named agent trigger fires, or on a matching message. Each choice reveals its own field group; Message Match adds a Message Source of Any, Human, Agent or a specific participant. |
| Condition Type | Always shown: Message Count (with a From filter of anyone, human, agent, or a named participant), Participant Message (a chosen participant sends anything), Agent Trigger (a trigger id fires), Any Response, or All Humans Responded. This is what actually fires the action. |
| Trigger ID Select from chamber triggers |
Wherever a rule refers to an agent trigger, the identifier is free text and the dropdown beside it is a convenience that writes the selected id into that box. The picker appears in up to three sections of the same modal, so it is worth noting which one is being filled; typed identifiers are not checked against the chamber. |
| Placeholder text when input is disabled | Disable Input only. The message shown in the composer while it is locked; defaults to “Please wait...”. |
| Trigger Agent Script (Optional) | Input actions only. Fires a named agent trigger alongside the enable/disable, after its own delay in milliseconds — the usual way to have a chatbot explain why the box has just locked. Leaving the trigger id empty skips it. |
| Timeout (ms) On Timeout |
End Section only. How long to wait for the condition before giving up, with 0 meaning wait indefinitely, and what to do when that time passes: force the section to end, or let the rule expire quietly. |
2.15.3 Display
The chat segment is the only type with no Display & Sharing card at all
— it has neither a display-mode dropdown nor a data-sharing dropdown, because
it is never the thing being displayed on top of something else. A chat is always
standalone, and the participant client refuses to render it in embedded mode even
if a configuration asks for one. The relationship runs the other way: a chat is the
background that embedded children are drawn over, either as an
inline card appended to the message stream or, with the child's embedded layout set
to split, in a pane beside a docked chat column
(§2). Below a viewport width of 900 pixels
every embedded child falls back to standalone, which means that on a narrow screen
the child replaces the chat instead of overlaying it — worth knowing
before recruiting participants on mobile devices. One further chat-only detail has
no control in the builder: a prompt value set on the segment through
an imported configuration renders as a standing banner above the message stream.
The chat panel is older than most of the builder, and four of its controls still save a value that nothing at runtime reads. They are worth knowing by name, because each one looks like a manipulation and none of them is:
Max Messages— no cap is applied. A study that needs a fixed number of turns has to reach the same end by other means: a control event with condition Message Count and action End Section, which does work.Show typing indicator— indicators are always on. The checkbox's only real effect is showing and hiding the two fields below it, so it cannot be used as a presence manipulation.Agent Think Delay Min/Max— not consulted, despite the “legacy fallback” help text. Response latency is set per chatbot in that participant's own configuration, and a delay typed here will not change it.Show message history from previous segments— does not gate what the stream shows.
Two adjacent settings do behave as labelled and are easy to confuse with the
above: Show participant list genuinely hides the sidebar roster,
and a chat left on Embedded complete whose children have since been
deleted or moved will never advance — the builder prints an amber warning
under the transition dropdown when that happens, and a
Fallback timeout is the insurance against it.
The researcher opens a chat segment's configuration, turns on behaviour tracking, adds a control event whose action is “End Section” with the condition “All Humans Responded” and a sixty-second timeout, and the new rule appears in the segment's Control Events list.
§2 · Segments
2.16 The social feed segment
The social segment renders a mock social-media
platform — a feed of researcher-authored posts with photos,
captions, like counts, and seeded “crowd” comments. The platform takes
over the entire screen for the duration of the segment:
participants see only the mock platform's own chrome (its top bar and feed), not
the Carrier segment UI, for a fully immersive simulation. Participants browse the
feed and can comment, reply, like or vote on posts and comments,
follow authors, and click decorative share/save buttons.
Three layouts are available per segment:
- Instagram-like — media-dominant cards, double-tap to like, caption below the image, collapsed comments. Replies nest one level.
- Facebook-like — caption-first cards, a labeled Like · Comment · Share bar, bubble comments. Replies nest one level.
- Reddit-like — a forum layout: an up/down vote gutter with a running score on every post and comment, title-led posts (a post can be an image post, a text post, or a link post), optional community and flair labels, and comment threads that nest to any depth. Indentation re-bases every five levels behind a “Continue this thread →” link, so a deep argument stays readable.
It is tempting to use layout as a between-subjects condition. Be aware that the three layouts do not record the same data and do not display the same stimulus, so a layout factor is confounded with both the response set and the content participants see:
- The reddit layout records a dependent variable —
vote scores, in the
_votesvariable and asvote_post/vote_commententries in_actionLog— that is structurally empty in the other two layouts, which have no vote control at all. An empty_votesthere means “never offered”, not “chose not to vote”. - The reddit layout can carry stimulus text the other layouts
never display:
title,community,flair, link posts (kind,linkUrl) and a seededscore. The same feed therefore presents different content in different arms. The Feed Builder warns you when a feed uses these fields on a non-reddit layout — it does not block the save, so layout stays swappable. Comment depth is the one exception: if your seeded comments nest more than one level deep, the Instagram and Facebook layouts cannot render them and the save is refused until the threads are flattened. There is now exactly one place to change layout — the control above the preview in the Feed Builder — so every layout change goes through that validation. (Before the Feed Builder was rebuilt, the segment card carried a second Layout dropdown that wrote straight through without re-validating; feeds authored that way may still hold deep threads on a shallow layout, and opening the Feed Builder and saving will surface it.) - Instagram and Facebook were never equivalent either,
long before the Reddit layout existed. A like made by double-tap
(
like_postwithvia: "double_tap") is Instagram only, andsave_post/unsave_postare missing from Facebook (they exist on Instagram and Reddit). So even an Instagram-vs-Facebook study is confounded: two logged actions are simply unavailable in the Facebook arm. Do not treat double-taps or saves as matched measures across those two. - To use layout as a clean condition, leave the reddit-only post fields empty and restrict your analysed measures to what every arm affords — comments, replies, likes, follows and dwell.
| Parameter | What it controls |
|---|---|
| Feed Builder → Layout | Chosen above the live preview, so the change is visible as you make it. One of Instagram-like (media-first, double-tap like), Facebook-like (caption-first, labeled action bar) or Reddit-like (forum threads, vote scores). It sets the platform’s chrome and card design, whether a vote gutter exists at all, and how deep comment threads may nest — one level on instagram and facebook, unbounded on reddit, where indentation re-bases every five levels behind a “Continue this thread →” link. It is therefore a capability switch, not a skin: see the caveat below. |
| Feed Builder → Feed settings → Participant handle | The username the participant appears to comment under; defaults to you. Accepts {{variable}} interpolation against the participant’s properties (§4.5), which is how a feed shows a per-condition or per-participant identity. A token that resolves to nothing is left on screen literally, so a mistyped name is visible rather than silent. |
| Segment inspector | Read-only, and deliberately so: layout, handle and the post and account editors all live in the Feed Builder, and repeating them here was two places to change one setting. It shows a summary card (layout, counts of posts, accounts, seed comments and live agents), an arrival strip plotting each post on the segment’s own clock with an amber marker at the end, the readiness checklist, and the managed variables the segment will record. Readiness is a ✓/⚠ list: agent accounts exist and are bound, each bound agent has a trigger that actually responds, an API key is stored for each LLM provider in use, and — the check most easily missed — the segment has a duration and no post is scheduled at or after it. A post arriving after the end is authored, exported and never seen. The unbound-account row carries an inline Set up an agent account button that opens the Feed Builder with the agent wizard already running. |
| Open Feed Builder | Opens the editor where the feed is authored, in three tabs. General is the workspace: the feed outline on the left (posts on an arrival timeline, then the cast of accounts beneath, each pane scrolling on its own so a long feed never buries the cast), a contextual editor in the middle for whatever is selected, and a live participant preview on the right rendered in the participant’s own stylesheet. Agents and Feed are detail tabs for the work a column beside a preview is the wrong shape for — see their rows below. A worked walkthrough of all three is in Appendix E. |
| Post → Media → Images | A collapsible library of uploaded images (PNG, JPEG, WebP, GIF) shared across the experiment, with multi-select upload, drag-and-drop and per-image delete. The server sanitises and de-duplicates filenames and the confirmation reports the stored name — that name, not the local one, is what a CSV must reference. Deleting an image a post still uses asks for confirmation. |
| Add post | Appends an empty post with a generated postId (p1, p2, …). The id is the identity used by agent scopes and by CSV rows and is not editable in the interface; renumbering means exporting, editing and re-importing. |
| Data → Import JSON / CSV…, Export JSON, Export CSV, CSV help, Starter kit, Template JSON | The file actions, gathered under Data in the Feed Builder header. CSV is a flat, spreadsheet-friendly form of the same feed: accounts are derived from author handles and existing agent bindings survive a re-import, so content can be rewritten in a spreadsheet without losing the agent wiring. Import blocks on a malformed file or a structural validation error, but not on binding problems or layout warnings, which are left to be fixed in the interface. The starter kit bundles a worked CSV, a plain-language guide and sample images; Template JSON downloads a worked configuration to bulk-edit from. |
| Agents tab | One agent account at a time, in full: its persona prompt, provider and model, temperature, max tokens, context window, whether it answers every comment or waits for a pause, its reply delay and its post scope. These fields belong to the chamber agent, not to the feed — this is the same agent card that sits on the builder canvas, so the edits apply immediately rather than waiting for the feed’s Save, and there is one copy of the persona rather than two to reconcile. An account bound to nothing shows the bind control instead of a form full of dead fields; an account bound to a scripted-rules agent says it has no persona to tune and points at the trigger editor rather than inventing an empty prompt for it. |
| Feed tab | Every post at once: a sortable table (id, author, caption, arrival, likes, replies) with row selection and bulk Retime, Reassign author and Delete — deleting also drops the post from any agent’s scope, so no agent is left scoped to a post that no longer exists. Above it, arrival on a real axis in three bands: a backlog post at −2h and a post at +45s cannot share one linear scale without collapsing the segment itself to a sliver, so the backlog and the overflow each get a compressed band and the live window keeps the width. The amber band is posts scheduled at or after the segment ends. A right-hand rail lists the managed variables a run will produce, with _votes struck through whenever the layout cannot collect it. |
| Outline → filter | Matches post id, caption and author, and reports “N of M” while active. It never renumbers anything, so it is safe to filter and then act on what is shown. Past eight distinct arrival times the timeline stops printing a header per time — a hundred posts at a hundred times would be a hundred single-post headers — and groups into Before entry · On entry · After entry, moving each post’s own offset onto its row. |
| Post → Author · Caption | The authoring account (chosen from the feed’s accounts — a post cannot be attributed to an ad-hoc handle) and the post body, which is required. Both accept {{variable}}. |
| Post → Title, Community, Flair, Score, Kind, Link URL | The forum fields: a headline above the body, a community label such as r/science, a flair chip, and a starting vote score that may be negative (left blank it falls back to Base likes rather than to zero). Kind selects image, text or link; text and link suppress the image in every layout, and link without a Link URL fails validation. The instagram and facebook layouts display none of these fields, which is what makes a feed that uses them layout-specific. |
| Post → Image | A source (preset, url, dataUri, and library once images have been uploaded) paired with a value control that changes shape to match — a preset picker, a library picker, or a free-text field. A library choice is stored as an ordinary URL, so it reads back as library only while the file is still in the library. An unknown preset name is a validation error. |
| Post → Base likes · Time label | Display only. Base likes is the starting count under the post; a participant’s own like adds one on their screen alone and is never shared with anyone else in the chamber. Time label is the cosmetic age (“3h”) and has nothing to do with when the post appears. |
| Post → Appears · Offset | When the post enters the feed, authored as a direction and a value in ms, seconds, minutes or hours rather than as raw milliseconds. Before entry places it in the backlog paged in by scrolling, At entry means already present, and After entry makes it arrive later behind the “new posts” pill. What is stored is unchanged — a signed whole-millisecond showAt, which is also the sort key for Feed order — and the field prints the stored value beneath it. |
| Post → Thread | The pre-written comments, edited as the thread they describe: each carries an author account, text, a like count and its own offset on the same clock, and Reply inserts a child beneath one. A seed comment never appears before its post, whatever its offset. Nesting depth is the one hard constraint the layouts impose, and the interface enforces it rather than letting the validator catch it: on instagram and facebook the Reply button is absent on a comment that is itself a reply. An empty comment is a validation error, so a comment added and left blank blocks the save. |
| Account → handle · kind | The visible username — duplicates are a validation error, and the avatar colour is derived from the string, so renaming an account recolours it — and whether the account is crowd (decorative; it authors posts and seed comments and does nothing at run time) or agent (live, and able to reply). Choosing crowd hides everything below. |
| Account → Replies as and New agent | Binds the account to one of this chamber’s agents by display name; only chamber agents are listed, and one agent may hold only one account — an agent already bound elsewhere is offered disabled, naming the account holding it, rather than being accepted and refused at save. An agent account left unbound is a validation error, so the feed will not save and the experiment will not deploy until it is bound or set back to crowd. The New agent button beside it runs a guided wizard that creates the agent — LLM persona or scripted keyword rules, with provider, model and personality prompt — adds it to the chamber and binds it in one pass. Participants never see the agent’s name, only the account handle. |
| Account → Responds on | All posts (whole feed) or Chosen posts…, which turns the feed timeline itself into a checklist — the posts are ticked where they are authored, so post ids never have to be matched by hand. All and None act on whatever the filter is showing, and Chosen only narrows the list to what has been ticked. A scoped agent never receives comments made elsewhere in the feed, and an LLM agent additionally gets a scope-aware identity block in its system prompt naming its handle and quoting its posts, so several agents can run as distinct personas owning different parts of the feed. Choosing Specific posts and ticking nothing is a validation error. |
| Account → Reply delay (ms) min / max | The reply is generated as soon as the comment arrives but held back for a random interval in this range, so it reads as human rather than instantaneous. Both blank means reply as soon as ready; the range must be whole milliseconds with min no greater than max, and a delay on a crowd account is rejected. |
| Feed settings → Feed order | Newest first, Oldest first (both sorting on Show at) or Manual, which preserves the order of the Posts list. Exposed only inside the Feed Builder. |
| Feed settings → Allow likes, Allow replies, Show share/save, Collapse seed comments | Four affordance switches, all on unless turned off. Turning off Allow replies makes the feed a pure exposure stimulus and leaves _comments and _commentCount empty by construction. Collapse seed comments decides whether seeded comments start folded behind a “View comments” toggle, as a real feed would, or fully expanded — a real manipulation of what participants read without an extra click, not a cosmetic one. Note that facebook has no save affordance at all, whatever Show share/save says. |
| Data Sharing | Offered, because social counts as an interactive type, but a poor fit: the segment submits seven evolving JSON collections rather than one comparable response, and nothing in the feed renders another participant’s state. Leave it at Never. |
The researcher opens the chamber, finds Social Feed in the Library, drags it onto the chamber timeline, and opens the Feed Builder to author posts and accounts.
Note: this recording predates the Feed Builder rebuild and shows the older four-tab editor and a Layout dropdown on the segment card. The path into the segment is unchanged; for the current editor see Appendix E.
Progression is time-based: new social segments default to a 3-minute auto-advance timer (adjust the duration in the segment's Timing panel), and the remaining time appears as a small pill in the platform's top bar that turns amber as the end approaches. If you configure a manual transition instead, a subtle “Continue →” button appears in the platform chrome.
The feed connects to the agent system through account binding:
each account in the feed is either crowd (decorative — it
authors posts and seeded comments only) or agent — bound to one
of the chamber's non-human participants by display name. When the participant
comments, the comment travels through the same pipeline as chat messages, so every
trigger type and LLM configuration works unchanged; the bound agent's reply
appears in the feed as its account, threaded under the comment that
triggered it. At segment start the platform writes one hidden context message per
post into the conversation history, so LLM agents know what the posts say without
any extra configuration.
Each agent account also has a scope: by default it responds
across the whole feed, or you can scope it to specific posts by ticking them on
the Feed Builder’s own timeline. Scoped agents only react to comments on their posts —
a comment elsewhere never reaches them — and unbound agents stay silent in
social segments. LLM agents additionally receive a scope-aware identity block in
their system prompt (“you are commenting as sona.fields; you
only take part in these posts: …” with the posts' captions and seeded
comments), so their replies stay in character and grounded in the right content.
This lets you run several agents as distinct personas, each owning different
posts.
The fastest way to set a persona up is the New agent button beside an account's bind dropdown: give it a name, a persona prompt, and a provider/model, and Carrier creates the agent (with an LLM reply-to-comments trigger), adds it to the chamber, and binds it — one step, no script builder needed. Afterwards, the Agents tab is where that persona is tuned: prompt, model, temperature, context window and timing, without leaving the Feed Builder to hunt for the agent on the canvas. A readiness checklist (agent accounts bound · responding triggers present · API key stored · every post arriving before the segment ends) appears both in the segment inspector and behind the chip in the Feed Builder’s header, and each agent's own config panel still shows where it is bound (“replies as sona.fields on posts p2”) or warns when it isn't.
Feed content is edited in the Feed Builder (posts, seed
comments, accounts, bindings, scopes, layout, settings). Its header’s
Data menu bundles the file actions —
import as JSON (the full configuration) or CSV (a flat, spreadsheet-friendly
content format — accounts are derived automatically from the author handles
and existing agent bindings survive re-import), export in either format —
with a Template JSON download of a worked example to start a
bulk edit from, plus a downloadable starter kit (worked CSV,
plain-language guide, and sample images) for spreadsheet-based authoring. Seven
managed variables are created automatically per
social segment — social_<segmentId>_comments,
_agentReplies, _likes, _follows,
_actionLog, _commentCount, and _votes
— and can be used anywhere other variables can.
_votes is only ever filled by the reddit layout; on the other two it
stays empty because there is nothing to vote with.
If you built a social segment before the reddit layout shipped,
it still has only the original six variables. Carrier does not add
_votes to a saved experiment on its own — open that segment's
Feed Builder and save (or re-import its CSV) and the variable
appears. Votes are recorded in _actionLog either way; it is only the
dedicated _votes variable that has to be created this once.
§2 · Segments
2.17 The task segment
In its plain form a task segment shows a block of instructions, then a
labelled text box with a live character counter reading “0 / 500
characters” and a Submit button that stays greyed out until the
response is long enough. Submitting locks the box, removes the button and leaves a
“Response submitted” confirmation in its place. Give the segment an
iFrame URL and an external page — an existing web experiment, a
game, a reaction-time tool — is loaded in a sandboxed frame between the
instructions and the text box. Turn on the custom application and the segment is
handed over entirely: the instructions shrink to a short Markdown header, the
researcher's own HTML fills the rest of the screen, and Carrier's text box, counter
and results panel are not rendered at all.
The task segment is the escape hatch for activities Carrier has no
purpose-built segment for, and it is used at three escalating levels: a prompted
free-text response with length limits; an externally hosted tool that posts a
result back when the participant finishes; or a full custom application whose
internal state is bound to experiment variables in both directions. That last case
is what distinguishes the segment from a dressed-up input. A belief
slider, a network diagram, an economic game or a drawing canvas can write its state
into variables that surveys, matching rules, visibility gates and bot prompts all
read (§4.2), and can be handed live
variables back as they change — so a bespoke instrument becomes part of the
experiment rather than an external detour whose data has to be reconciled
afterwards. The trade-off is that whatever runs inside the frame is the
researcher's responsibility: Carrier renders it, sandboxes it and records what it
posts back, but validates none of it.
| Parameter | What it controls |
|---|---|
| Instructions (shown to participants) | The text above the task; empty falls back to “Complete the task below:”. Injected as raw HTML in the plain task — markup works — but rendered through the restricted Markdown renderer when the custom application is enabled. One field, two rendering rules. |
| Require text submission | On by default. Renders the prompt, text box and Submit button, and holds the segment's Continue button disabled until Submit is pressed. It is the gate, not just the visibility switch — see the caution below. |
| Text Prompt | The label shown above the text box (default “Enter your response below:”). The box's own placeholder, “Type your response here…”, is fixed and cannot be changed. |
| Min Length · Max Length | Minimum characters before Submit becomes clickable, and a hard maxlength ceiling on the box (empty = no limit). Both surface only in the counter, as “N / max characters” with a small “(min N)” hint; there is no message explaining a greyed-out Submit, and typing simply stops at the maximum. |
| iFrame URL | An external tool loaded in a sandboxed frame. It returns its result by posting { type: 'carrier-task-result', payload } to the parent window; the payload is stored alongside the text response as iframeData. |
| iFrame Height (px) | Fixed pixel height of that frame (default 400, minimum 100). Pixels here — the custom application's height is in vh. |
| Enable custom HTML application | Replaces the whole plain-task rendering with the researcher's HTML and reveals the fields below. Once it is on, iFrame URL, Text Prompt and the length limits no longer reach the participant. |
| Source · URL · Inline HTML (N chars) | Where the app comes from. External URL exposes a URL field — a different field from iFrame URL above, with different plumbing. Inline HTML exposes a character count plus Edit HTML… (a full-screen editor) and Insert starter template. |
| Height (vh) | Height of the application frame as a percentage of the viewport, 10–100, default 80. Around 80–100 fills the segment. |
| Hide advance button | Suppresses the Continue button outright, ahead of every transition rule, so an immersive app can fill the segment and let the parent chat drive progression. |
| Variable bindings | The table of OUT and IN rows that connects the app's state to experiment variables. Each row is a direction, an html key (the field name inside the app's own state object) and a variable; a live cheat-sheet below prints the exact messages the app must send and listen for. |
| Data Sharing | Offered (Never / After all submit / Real-time) because task counts as an interactive type, but for a task the shared results panel only ever reads “Submissions — N response(s) submitted”. The Results Display and Anonymous Results sub-options that appear beneath it change nothing on a task, because no response content is shown. |
| Display Mode | Standalone or embedded, with the usual embedded start / stop / completion and layout controls. Choosing embedded removes the Timing and Transition cards from the inspector. |
| Timing · Transition (defaults) | A task is not treated as user-paced, so a new one is created with a 5-minute duration, a 60-second warning and transition mode sync — a timed segment that waits for the whole group. Worth revisiting deliberately, because it interacts with the submission gate below. |
The bindings deserve a closer look, because they are what the custom application is for. An OUT row names a new variable and Carrier creates it: a managed alias that appears in the Variables Manager locked — referenceable everywhere, editable nowhere — and is dropped again if the binding row is removed. A name already in use is refused with an inline warning rather than silently overwritten. An IN row picks from a dropdown of the experiment's variables, deliberately listing all of them rather than only those already resolved at this point in the timeline, because the interesting IN values are often ones that change during the segment — an LLM-processed variable, a running peer aggregate.
The contract on the other side of the frame is two messages. The app posts
{ type: 'carrier-task-data', state: {…} } whenever its state
changes; Carrier picks out the OUT-bound keys, debounces them over 400 ms,
discards no-op repeats and flushes whatever is pending when the segment ends. In
the other direction Carrier posts carrier-variables with the changed
IN keys. Variables that were already set before the segment began never fire a
change event, so an app that wants them must announce itself with a
carrier-app-ready message and receive the full snapshot in reply.
Insert starter template writes a working skeleton that already does all of
this, wired to the bindings exactly as they stand at that moment — so add the
bindings first, generate the template second, and note that it overwrites any
existing HTML without asking.
A task can run standalone or embedded, and both embedded layouts are fully supported: the inline card rewires the submit, iframe and custom-application listeners exactly as the standalone path does. The defaults are standalone and inline. The split layout — the app in the main pane with a live chat docked beside it — is the intended shape for an instrument used during a discussion, but it needs a chat segment as its parent; without one it falls back to the inline card silently. Below 900 px of viewport width every embedded segment reverts to standalone, which for a task means the app replaces the conversation rather than sitting next to it.
Most task segments that go wrong in a pilot go wrong in one of these five ways. All five are silent — the participant sees no error.
- Submit is rendered disabled and is only re-evaluated when the participant types. Min Length 0 does not mean an empty response is acceptable: a participant who types nothing never enables Submit, and the Continue button stays disabled behind it.
- With the default transition mode sync, that participant does not merely strand themselves — the whole matched group waits until the 5-minute duration expires.
- Require text submission is inert once the custom application is enabled, because no text box is rendered to gate on. The app's own state, delivered through OUT bindings, is then the only record of what the participant did.
- A
carrier-task-resultpayload larger than 1 MB, or one that cannot be serialised, is replaced by a marker recording that it was oversized. Sending it would exceed the server's message limit and disconnect the participant mid-run, so the run is protected at the cost of the payload. Post ids, scores and timings; upload large artefacts over HTTP separately. - Sandboxing differs by origin, and not in the direction most people expect. A cross-origin tool keeps
allow-same-origin; a tool served from Carrier's own origin deliberately loses it, and inline custom HTML runs in an opaque origin with no cookies, nolocalStorageandevent.originreported as the string “null”. A tool that depends on its own storage will not work in the same-origin or inline cases.
The researcher switches a segment to task, enables the custom HTML application, adds an OUT binding and an IN binding to the bindings table, clicks “Insert starter template”, and opens the HTML editor to show the generated skeleton already posting and listening on exactly those keys.
§2 · Segments
2.18 The process segment
A process segment shows a spinning icon and a single line of text
— by default “Analyzing your responses…” — centred on
an otherwise empty screen. There is no input, no Continue button, and no
navigation of any kind: the participant waits, and the platform moves them on when
it is ready. It is the only segment type that asks nothing at all of the person in
front of it.
Its purpose is the work happening out of sight. While the spinner turns, the server
runs the llm-processed variables attached to the segment: it sends the
sources those variables declare — a free-text answer, an earlier submission, a
chat transcript — to a language model with the researcher's prompt, and stores
the result against each participant. A process segment is therefore the join between
something a participant has already done and something a later segment needs to
know: a summary of an open-ended answer to read back to them, a classified stance
used to gate a chamber, a piece of advice written into an instruction. It is
plumbing rather than an activity, and it exists for one reason — the timeline
must not advance until that derived value exists.
| Parameter | What it controls |
|---|---|
| LLM-processed Variables | One tick-box per variable in the experiment whose kind is llm-processed. The ticked variables are the ones this segment runs and waits for. Every such variable is offered regardless of its own Update trigger, and ticking it runs it here whatever that trigger says. If the experiment defines none, the control collapses to the placeholder “No llm-processed variables defined. Create one in the Variables manager first.” — the segment cannot be configured at all until a variable exists (§4.2). |
| Processing Message | The line of text under the spinner. Left empty it reads “Analyzing your responses…”. The server sends its own copy again when the segment starts and overwrites what the browser rendered, so a message containing variable references resolves server-side and may visibly change a beat after the spinner appears. |
| Timeout (seconds) | How long the server waits for the language model before applying On Timeout. Default 30, minimum 1. It also sets the participant's release: the browser advances on its own five seconds after this figure if the server's completion signal never arrives. Emptying the box restores 30 s rather than removing the limit — there is no “wait forever” setting. |
| On Timeout | What becomes of variables that have not resolved when the timeout expires. Use default values (fallback) writes each unresolved variable's default value for every participant who has none; Block (keep waiting) writes nothing and leaves them unset. Neither option keeps the participant on the spinner — see below. |
| Auto-advance when processing completes | Ticked by default. When ticked, the completion signal tells the browser to move to the next segment as soon as the variables resolve. Unticking it changes the signal to “wait”, which the browser honours — but the segment offers no control with which to continue, so the participant is released by the timeout instead. See below. |
Everything about the language-model call itself — provider, model, system and user prompts, declared sources, output format, and whether the call is made once per participant or once per chamber — belongs to the variable definition in the Variables manager (§4.2), not to the segment. The segment contributes only three things: when the call happens, how long the study will wait for it, and what the participant sees meanwhile. One consequence is worth knowing before debugging an empty result: if a ticked variable carries a capture scope pointing at some other chamber or segment, the process segment skips it silently and its stored value stays frozen.
The builder's validation panel flags a process segment with nothing ticked
(“nothing will be processed”), and flags a tick that no longer
corresponds to an existing llm-processed variable — renaming or
deleting a variable in the Variables manager leaves the segment holding a dangling
reference, which is reported but not repaired.
2.18.1 Display and timing
Treat the process segment as standalone only. The builder's
Display & Sharing card nonetheless offers it a Standalone / Embedded
choice, because embedding is withheld only from chat and
attention-check; the participant-side renderer, however, has no case
for this type. An embedded process segment renders the literal text
“Unknown segment type: process” inside the chat flow and the variables
never run at all. (Below a 900 px viewport the runtime falls back to standalone
and the segment works by accident, which makes the fault easy to miss on a laptop.)
Leave Display Mode alone. There is no Data Sharing control, since
nothing is submitted here.
Timing is collective. The server starts the run only once every participant in the chamber has reached the segment, and broadcasts completion to the whole chatroom, so a matched group waits together and leaves together. In a single-participant chamber the wait is simply the length of the call.
The panel reads as though the researcher controls how long a participant is held. In practice the participant is always released, and the settings decide only what has been written by the time they are:
- Block (keep waiting) does not keep anyone waiting. The server races the model call against the timeout and announces completion when the timeout wins, exactly as it does under fallback. The sole difference is that block writes no default values, so an unresolved variable is left empty — and a later instruction or prompt interpolating it renders nothing. Of the two options, this is usually the more damaging failure.
- Unticking Auto-advance does not produce a pause. It changes the completion signal to “wait”, and the browser obeys, but the segment draws no continue control of any kind, so the participant sits on the spinner until the client's own release fires at Timeout + 5 s. The net effect is to replace a prompt advance with a silent, later one. There is no supported way to hold a participant on a process segment, and no way for them to advance by hand.
- The Timing and Transition panels are effectively bypassed. Progression is driven by the completion signal and that release timer, whichever comes first. A newly dropped segment carries the generic 5-minute Max Duration, which only ever takes effect if it is shorter than Timeout + 5 s; Manual mode and Allow early advance have no button to expose. Lengthening the wait means raising Timeout, not Max Duration.
- The two clocks do not start together. A participant's release timer is armed the moment they reach the segment, while the server begins the run only when the last participant arrives. In a chamber with staggered arrivals an early arriver's clock can therefore run down before the call has even started. Set Timeout generously in multi-participant chambers.
- Changing the segment's Type discards this configuration. A process segment stores its settings apart from every other segment type's configuration, and that store is deleted the moment the type changes. Switching a segment to another type and back returns the ticks, message, timeout and timeout behaviour to their defaults, without warning.
The researcher drags Process (LLM) onto the chamber timeline and finds its variable list empty — “No llm-processed variables defined” — so they open the Variables manager, add an llm-processed variable with a source and a prompt, and return to the segment, where the new key now appears as a tick-box beside the processing message and timeout.
§2 · Segments
2.19 The attention-check segment
An attention-check segment takes over the screen with a panel headed
“Attention Verification” and asks the participant to prove they are
present and paying attention before the timeline will let them continue. What that
proof looks like depends on the mode. In survey mode the panel is a
short Survey.js form of researcher-written items — instructional-manipulation
checks, catch trials, comprehension questions — graded the moment the
participant presses Survey.js’s own Complete button. In
liveness mode the participant first sees a consent-style
pre-screen (“This step requires camera access to verify your presence”
/ “No video will be recorded or transmitted”) with a Begin
Verification button, then a live camera preview that calibrates, waits for a
face to hold steady in frame behind a filling progress bar, and finally requests one
physical action at a time — blink, turn left, raise eyebrows, and so on. In
CAPTCHA mode the participant meets an animated canvas and a
countdown: either counting circles in a phase-coded animation, clicking a red dot
before it vanishes, or watching a masked ball trace a hidden path and then picking
every matching trajectory out of a thumbnail grid.
The purpose is screening, and the segment is built around what happens to a participant who fails rather than around the check itself. Every attempt emits a result record — mode, pass/fail, retry count, and mode-specific details — which lands both on the run and as a system message interleaved into the transcript, so a failure is visible in the export at the moment it happened (see §6.5). The failure itself routes through a configurable policy: end the session with a message and a redirect to a panel screen-out URL, grant a fixed number of retries, or log the failure and carry on so that attentiveness can be used as a covariate rather than an exclusion. The three CAPTCHA variants exist for a narrower reason: their answers are generated and graded server-side, so unlike a survey item they cannot be passed by a script that has read the experiment configuration.
2.19.1 Mode
Mode is the first control on the panel and selects the whole check
family — Survey (question-based) (the default),
Liveness (camera-based), or CAPTCHA (perception / reaction).
Changing it rebuilds the rest of the panel, but the configuration for all three
families is kept side by side in the segment, so switching mode to look at the other
options does not discard the settings already entered.
2.19.2 Survey mode
| Parameter | What it controls |
|---|---|
Questions / Add Question | The list of graded items. Each is a collapsible card in the builder, headed “Q1: …”, with a trash button to remove it. An empty list is a configuration error the runtime does not report — see the caveat below. |
Question Type | Per item: Radio Group, Checkbox, Dropdown, Text, or Rating. The choice list is hidden for Text and Rating, which have none. |
Question Title | The prompt shown to the participant, and the card’s header in the builder. |
Question Name (unique ID) | The key this item is recorded under in the graded response data and in the per-attempt result record. Auto-seeded as attn_q0, attn_q1, … Two items sharing a name collide in the same key, and this is also the field a completion condition matches on. |
Choices (one per line) | The answer options, newline-separated. Shown for Radio Group, Checkbox and Dropdown only. |
Correct Answer and Case sensitive | The expected value for this item. Grading is a string comparison, lower-cased on both sides unless Case sensitive is ticked — that checkbox appears for Text items only. Because the comparison is on the stringified answer, a Checkbox item’s multi-select answer must be written here as a comma-joined list in the order the options are stored. |
Pass Criteria and Pass Percentage | All correct (the default) or Percentage. Choosing Percentage reveals a 0–100 threshold, default 100, compared with “at least” — so a threshold of 100 is exactly equivalent to All correct. |
Randomize question order | On by default. Shuffles the presentation order per participant. Grading is keyed on Question Name, so shuffling never misaligns an answer from its expected value. |
Show feedback on wrong answers | Intended to append a per-item Correct / Incorrect list under the form when the participant fails. In practice it is never seen — see the caveat below. |
2.19.3 Liveness mode
Liveness mode has two phases in two accordions, each independently switchable. The presence phase establishes that a face is in front of the camera at all; the liveness phase establishes that it is a live one by requesting actions a static photograph cannot perform.
| Parameter | What it controls |
|---|---|
Enable presence check | On by default. Requires a sustained face in frame before the action phase begins. |
Sustained Duration (seconds) | How long a face must be continuously detected to clear the presence phase. Default 3 s. The phase also carries an internal 60 s ceiling that is not exposed in the builder. |
Enable liveness check | On by default. Runs the requested-action challenge after presence. Turning both phases off leaves a check with nothing to verify. |
Action Pool | Seven checkboxes — blink, turn-left, turn-right, raise-eyebrows, open-mouth, nod-up, nod-down — selecting which actions may be requested. All on by default. The labels are the raw identifiers, not prose. |
Number of Actions | How many actions are drawn at random from the pool, 1–8, default 3. The builder does not clamp this to the number of actions actually ticked. |
Time Limit (seconds) | The budget for the whole action phase. Default 30 s, minimum 5 s. |
Instruction Text | The status line shown during a phase. There are two identically-labelled fields, one per accordion: the presence phase defaults to “Please position your face in the camera frame”, the action phase to “Please perform the following actions”. |
A camera permission the participant declines is treated as a retryable problem, not
a failure: the panel shows a warning and offers another attempt rather than
terminating the run. A passing liveness check is also the only thing that can start
a chamber’s Background Face Monitoring — that setting’s
Start After Segment dropdown lists attention-check segments
and nothing else (see §1.3).
2.19.4 CAPTCHA mode
CAPTCHA Variant selects one of three challenges, each with its own
parameter set and its own server-side grader. Changing the variant rebuilds the
sub-panel.
| Parameter | What it controls |
|---|---|
CAPTCHA Variant | Spooky Circle (perception) — count the circles in a phase-coded animation; Red Dot (reaction) — click a red target before it disappears; Trajectory Recovery (motion) — identify a masked ball’s path in a thumbnail grid. |
Spooky Circle · Minimum Circle Count and Maximum Circle Count | The range a generated challenge may draw from, 1–9 each, default 2 and 7. They also become the bounds on the participant’s number input. The builder does not check that the minimum is below the maximum. |
Spooky Circle · Time Limit (seconds) | How long the participant has to answer. Default 35 s. This is the only variant with its own time-limit control: Red Dot uses Overall Timeout instead, and Trajectory Recovery has no control at all and falls back to a fixed 40 s. |
Spooky Circle · Difficulty | 1–10, default 4. Higher values make the rings harder to perceive. |
Spooky Circle · Log answer telemetry (latency / input method) | Off by default. Records soft behavioural signals alongside the answer. It never contributes to the pass/fail decision. |
Red Dot · Required Hits | How many dots must be clicked successfully, 1–20, default 5. Drives the on-screen “0 / N” counter. |
Red Dot · Target Lifetime (ms) | How long each dot stays visible before vanishing, 200–5000 ms, default 800. Note the unit: this is the one duration field on the segment expressed in milliseconds rather than seconds. |
Red Dot · Overall Timeout (seconds) | The total budget for reaching the required hits. Default 60 s. |
Red Dot · Distractor Count | Decoy dots shown alongside the target, 0–10, default 0 (off). Every hit’s position and timing is verified server-side; the browser only reports the click. |
Trajectory Recovery · Grid Columns and Grid Rows | The size of the thumbnail grid, 2–6 each, default 4 × 4. |
Trajectory Recovery · Correct Trajectories | How many thumbnails match the ball’s path, default 3. The participant must select all of them. Its upper bound is derived from the current grid size, so it is re-clamped only when the panel rebuilds — check it after shrinking the grid. |
Trajectory Recovery · Enabled Trajectory Types | Which path families the target and the distractors are drawn from: Zigzag, Circular, Wave, Cubic, Quadratic. All on by default. |
Trajectory Recovery · Difficulty | 1–10, default 5. Higher values make the masked motion harder to follow. This is a separate control from the Spooky Circle difficulty of the same name and means something different. |
2.19.5 What happens on failure
These controls sit below the mode-specific panel and apply to all three modes identically.
| Parameter | What it controls |
|---|---|
On Fail | Terminate session (the default) ends the run with attention_check_failed as the recorded reason; Allow retry re-renders the check from scratch, drawing a genuinely new CAPTCHA challenge where applicable; Continue (log only) advances as if the participant had passed, leaving the failure in the data as a covariate. |
Max Retries | 1–5, default 1. Rendered only when On Fail is Allow retry. Retries are counted per participant and reported in the result record. |
Fail Message | The text shown on the end-of-study screen, which is titled “Study Ended” rather than “Study Complete”. Default: “You did not pass the attention check.” Hidden when On Fail is Continue (log only). |
Redirect URL (optional) | Where a failed participant is sent, typically a recruitment panel’s screen-out URL. The redirect fires after the participant acknowledges the debrief screen, not the instant they fail, so a participant who closes the tab first is never redirected. Hidden when On Fail is Continue (log only). |
Completion Conditions / Add Condition | A second, independent screen-out path, offered on this type and on survey only. Each condition is an If field (matched against a Question Name, exactly), an operator (=, ≠, in, >, <), a value, and its own message and redirect URL. It runs alongside the graded pass/fail rather than replacing it, which makes it the right place for exclusions that are not about attention — a demographic screen-out, say, on the same form. |
2.19.6 Display mode and timing
The attention check is standalone only, and this is enforced on both sides. The builder never offers a Display Mode dropdown for this type — its Display & Sharing card is empty, with no embedded-start triggers and no data-sharing control — and the runtime has no embedded renderer for it, so it can never be layered over a chat. That is a deliberate constraint rather than an omission: an attention check that could be dismissed like an overlay, or answered while a conversation continued underneath it, would not be measuring attention. The two common timing fields, however, behave differently here than anywhere else, and both are worth setting explicitly.
On every other segment type, the timing fields govern pacing. On this one they govern outcomes:
Max Durationexpiry is a failure, not an advance. A participant who runs out of time is recorded as failing with reasontimeoutand routed throughOn Fail— which, at its default, ends their session. This matters because of what a new segment inherits: attention checks are not one of the user-paced types, so a freshly added segment arrives with a five-minute duration already set, and switching the Type dropdown resets only the type-specific configuration, never the timing. An attention check left at its inherited default silently terminates any participant who takes longer than five minutes — a real risk for the camera and CAPTCHA modes, where a permission prompt or a retry can eat the budget. Set the duration deliberately or clear it.Min Durationholds participants who have already passed. A pass does not advance until the minimum has elapsed, so the participant sits on a completed check with nothing to do and no explanation. Leave it at zero unless the delay is the point.
Survey mode is the default and the easiest to leave half-configured. Each of these produces a check that is uniform across participants — and therefore measures nothing — without reporting a problem in the builder:
- No questions means everyone passes. A survey-mode check
with an empty
Questionslist is skipped at runtime with only a browser-console warning; every participant is waved through and no result record is written. A check added late in the build and never populated is indistinguishable, in the export, from one that was never added. - A blank
Correct Answermeans everyone fails that item. Leaving the field empty does not make an item ungraded — it makes the expected answer the empty string, which nothing the participant can select will match. With the defaultAll correctcriterion and the defaultTerminate sessionpolicy, one forgotten answer key ends every run in the study. To include an ungraded question on the same screen, put it in a separatesurveysegment instead. Show feedback on wrong answersdoes nothing observable. The feedback list is built into the page and the failure is handled in the same instant, so the panel is torn down — re-rendered for a retry, or replaced by the end-of-study screen — before the feedback is ever painted. Do not rely on it to tell participants why they failed; put that explanation inFail Message.
The researcher adds a segment, switches its type to Attention Check, adds two graded questions with their correct answers, clears the inherited five-minute Max Duration, and sets On Fail to “Terminate session” with a fail message and a screen-out redirect — and the Display & Sharing card stays empty throughout, because the check can only run standalone.
Roles — who occupies the slots a chamber defines, and what each kind of occupant may do inside these activities — are the subject of §3.
Part I · §3. Roles: Communicator, Mediator, Processor
Three orthogonal roles — communicate, facilitate, assist composition — each occupying a distinct zone of the participant's screen. Any type (human, LLM chatbot, scripted chatbot, agent) can fill any role, with one exception.
§3 · Roles
3.1 Why roles exist
Chapter 1 defined the shape of an experiment but not its cast. A chamber declares a list of slots, each of which awaits a participant; the question this chapter answers is what a participant who fills a slot can actually do.
Carrier separates that question into two orthogonal axes:
- The type of a participant — what they are. Four types: a real human, an LLM chatbot (language-model-driven, chat only), a scripted chatbot (rule-driven, chat only), or an agent (an autonomous Claude Agent with built-in tools for reading documents, running code, and browsing the web — see the note on non-human participants).
- The role of a participant — how they take part. Three roles: a communicator, a mediator, or a processor.
The four kinds of participant differ in two practical respects — how they produce what they say, and how reproducible they are across sessions:
| Kind | How it produces output | Reproducibility |
|---|---|---|
| Human | The person types | Whatever the person does |
| LLM chatbot | An LLM is called once per turn with the conversation history; output is a chat message or silence | Variable across sessions |
| Scripted chatbot | Pre-written rules fire when their trigger conditions match | Identical across sessions |
| Agent | The Claude Agent loops between LLM calls and built-in tool calls (read files, run code, browse) until it decides it is ready to speak, then produces a chat message grounded in what it found | Variable across sessions |
The type × role matrix is the most important table in this chapter, because almost every design decision below either depends on it or is constrained by it:
| Type \ Role | Communicator | Mediator | Processor |
|---|---|---|---|
| Human | ✓ | ✓ | ✓ |
| LLM chatbot | ✓ | ✓ | ✓ |
| Scripted chatbot | ✓ | ✓ | — |
| Agent | ✓ | ✓ | ✓ |
The only forbidden combination is scripted chatbot as processor. The reason is technical but worth knowing: processors operate by reading drafts, generating suggestions, or interrupting composition — activities that demand the kind of open-ended language understanding only a human or a language model can provide. A pre-scripted rule set has no business being a processor.
Everything else is supported. That makes it possible to write one experimental design and instantiate the same role with a human in one condition and a language model in another — which is the single most important affordance Carrier offers for studies that compare human and machine behaviour. It also means that the choice between an LLM chatbot, a scripted chatbot, and an agent can itself be the manipulation: same role, same instructions, three different kinds of non-human partner — one talking from training-time knowledge, one talking from a written rulebook, one talking from documents it has just read.
§3 · Roles
3.2 The spatial model: three zones in the interface
A useful way to keep the roles separate in your mind is to remember that each occupies a distinct zone of the participant's screen during a chat segment:
This is more than visual hygiene. Mediators broadcast; communicators converse; processors assist composition before words enter the conversation. The separation of channel is what makes it possible to study facilitation, conversation, and composition assistance independently of one another — or to combine them deliberately, knowing that the layers do not bleed into each other.
§3 · Roles
3.3 Communicator
3.3.1 Framing
The communicator is the primary interactive participant. Whatever the experiment ultimately studies, communicators are the ones doing the studied behaviour. All communicators — whether a human, an LLM chatbot, a scripted chatbot, or an agent — operate in the same message space: their messages appear in the main chat area alongside each other in the order they were sent.
The animating design principle is interaction parity. A human communicator and any non-human communicator send messages through the same mechanism, appear in the same UI, and are indistinguishable to other participants unless the researcher explicitly marks them otherwise. This is what makes it possible to run human–human, human–machine, and machine–machine conditions of the same design without rebuilding the experiment.
3.3.2 The communicator design surface
Three dimensions of configuration matter for any communicator. They are independent: settings on one dimension do not constrain settings on the others.
Identity. Who appears in the chat, by what name, and with what disclosure?
| Aspect | Choices | What you decide |
|---|---|---|
| Source of identity | User-provided · Configured · Auto-generated | Whether the participant chooses their own display name, you pre-set it, or the platform invents one. |
| Visibility | Visible · Hidden | Whether the communicator appears in the participant list at all. |
| Type disclosure | Disclosed · Blinded | Whether other participants are told that this communicator is human or AI. |
Human communicators typically go through an identity-setup flow (choose a display name, pick an avatar) before entering the experiment. Non-human communicators — LLM chatbots, scripted chatbots, and agents alike — carry pre-configured identities. The separation makes blinding possible: a participant cannot tell from the interface alone whether a fellow communicator is human or a machine.
Input control. When can the communicator speak, and who decides?
| Aspect | Choices | What you decide |
|---|---|---|
| Initial state | Enabled · Delayed · Conditional | Whether the communicator can send messages from the moment the chat begins. |
| Enable conditions | Time-based · Message-count · Bot-trigger · Participant-message | What event lifts a delay or unlocks input. |
| External control | None · Mediator-controlled | Whether a mediator can disable or enable this communicator's input dynamically during the chat. |
Carrier's chat input is not simply on or off. A communicator might begin with input disabled, wait for three messages from other participants, and then become enabled. Or a mediator (see §3.4) might disable and re-enable input on the fly to enforce turn-taking. Building experimental conditions out of these primitives is how you produce interventions like simultaneous discussion vs. sequential discussion.
Message capabilities. What kinds of messages can be sent and acted on?
| Aspect | Choices | What you decide |
|---|---|---|
| Content types | Text · Media (audio/video) | What the communicator can attach to a message. |
| Reactions | Enabled · Disabled | Whether emoji reactions are available to the communicators. |
| Reporting | Enabled · Disabled | Whether a participant can flag a message for the experimenter. |
3.3.3 Communicator subtypes at a glance
The four communicator subtypes correspond to the four types in the matrix:
- Human communicator. A real participant joining via browser. They enter the matching queue, are matched into a chamber, and join its chatroom. The platform tracks their socket connection with a heartbeat; on disconnect they can be reconnected within the session. All messages, survey responses, timestamps, and activity events are recorded.
- LLM-chatbot communicator. Configured by provider (OpenAI / Anthropic / Google / compatible), model, system prompt, temperature, and response logic (when to speak, when to stay silent — see §5.3). The model is given the conversation so far on each turn and produces a chat message (or stays silent). LLM chatbots never enter the matching queue: once the human slots in a chamber are filled, they are spawned into the chatroom automatically. The platform supports multi-step LLM chains for advanced configurations (a generation step, then a critique step, then a rewrite step).
- Scripted-chatbot communicator. Configured by a set of triggers (§5). Like LLM chatbots, scripted chatbots do not enter the matching queue — they are spawned into the chatroom after human slots are filled. Their behaviour is deterministic and replayable: the same input sequence produces the same outputs across sessions, which makes them the right choice for confederate roles and any design in which conversational reproducibility matters.
- Agent communicator. A Claude Agent (Anthropic) scoped to a particular document area — typically the study materials, a reference corpus, or a configured dataset. Before producing each message, the agent's underlying model loops over its built-in tools (file reading, code execution, web browsing) to look things up, run small computations, or check a citation. The message it eventually sends is grounded in what it has retrieved. Agent communicators are the natural choice when the experiment wants a conversational partner that can answer with evidence — a study-materials expert that quotes the brief verbatim, a fact-checker that can browse during the discussion, a domain assistant that can re-read the dataset before stating a number.
3.3.4 Research uses of the communicator role
A short, indicative list of designs that map cleanly onto different communicator configurations:
| Design | Communicator configuration | What it studies |
|---|---|---|
| Group discussion | 2+ human communicators | Opinion formation, group dynamics, polarisation |
| Human–AI dyad | 1 human + 1 LLM communicator | Trust, persuasion, perception of machine partners |
| Confederated AI | 1 human + N LLM communicators, blinded | Conformity, majority influence |
| Turn-taking study | Humans with delayed input control | Sequential vs. simultaneous discussion |
| Agent-to-agent comparison | 2 LLM communicators with different system prompts | Model behaviour under controlled prompting |
§3 · Roles
3.4 Mediator
3.4.1 Framing
The mediator is a facilitator. Unlike a communicator, a mediator does not converse on equal footing with the others — they orchestrate the conversation. The qualitative differences are four:
- Universal visibility. A mediator sees every message in the chat, regardless of who it was addressed to.
- Distinct delivery. A mediator's output appears as styled announcements at the top of the chat, not as chat bubbles.
- Control capabilities. A mediator can act on the chat — disable a communicator's input, prompt a specific participant, highlight a message — not only speak into it.
- Event awareness. A mediator reacts to aggregate patterns (number of messages, time elapsed, idle participants) at least as readily as to individual messages.
These four capabilities together describe the moderator / facilitator / researcher dynamic that has no analogue in plain group chat.
3.4.2 The broadcast system
Mediator messages are called broadcasts. A broadcast is styled along three independent axes:
| Axis | Choices | Effect |
|---|---|---|
| Style | Facilitator · Announcement · System | Icon and tone of voice in the rendered banner. |
| Priority | Normal · Important · Urgent | Visual emphasis and how long the banner stays before auto-dismissing. |
| Persistence | Dismissible · Persistent | Whether the participant can dismiss the banner. |
Broadcasts can also be targeted: at every participant in the chamber, at communicators only, at a specific named participant, or at all participants in a specific role. This is the mechanism by which a mediator can deliver a private prompt to one communicator without the rest of the group seeing it.
3.4.3 Facilitation actions (LLM-driven mediators)
When the mediator is driven by a language model — either an LLM chatbot or a Claude Agent — it has access to an action vocabulary that goes beyond plain broadcasting. These five intervention actions are produced as part of the model's structured response on each turn; the most consequential is the first.
A mediator implemented as a scripted chatbot can fire the same actions, but their parameters must be baked into the trigger configuration ahead of time (§5.5); the scripted bot cannot choose the action contextually based on what was just said.
Note that this intervention-action vocabulary — disable_chat,
enable_chat, prompt_participant,
highlight_message, request_attention — is distinct
from the built-in tools of a Claude Agent (file reading, code
execution, web browsing). They live on different channels:
- The intervention actions are how a mediator acts on the chat — they affect what participants see and what they are allowed to do.
- An agent's built-in tools are how it gathers information for itself — they affect what the agent knows when it speaks, but the participants see only the eventual message.
A Claude Agent acting as mediator has access to both: it can read its configured document area before responding, and fire intervention actions alongside its broadcast. An LLM chatbot acting as mediator has only the intervention actions; a scripted chatbot acting as mediator has only the pre-baked variants.
| Action | Target | Description |
|---|---|---|
disable_chat | A specific communicator, or all | Temporarily prevents the target from sending messages. The release is governed by a set of conditions described below. |
enable_chat | A specific communicator, or all | Explicitly lifts a disable. Immediate. |
prompt_participant | A specific communicator | Sends a private encouragement or prompt visible only to that participant. |
highlight_message | A specific message | Marks a past message as highlighted in the chat for a configurable duration. |
request_attention | A specific communicator | Triggers a visual or audio cue to draw the participant's attention. |
The disable_chat action supports composite release conditions
— a list of conditions combined with an any / all
connector that determines when the disable lifts. The available conditions are:
- Timeout — after a fixed duration.
- All others responded — when every other communicator has sent a message of at least a configured minimum length.
- Message count — after a fixed total number of messages have been sent in the chat.
- Keyword mentioned — when a designated keyword (or any from a list) is uttered by any, or a specific, participant.
- Participant message — when a designated participant has sent a configured number of messages.
- Mediator release — released only by an explicit subsequent
enable_chat. - Segment change — released when the chamber transitions to its next segment.
Composite release conditions are the building blocks for richly specified
turn-taking protocols. “Wait until everyone else has responded, or sixty
seconds, whichever comes first” is a single disable_chat action
with two conditions and an any connector.
3.4.4 Mediator-specific triggers
Mediators inherit the standard trigger system (§5), but five extra trigger types are particularly suited to facilitation:
| Trigger | When it fires | Typical use |
|---|---|---|
| Periodic | At regular intervals after the chat begins | Recurring summaries, scheduled check-ins. |
| Aggregate | After N messages have accumulated within a time window | Batched synthesis or pattern detection. |
| Topic-detected | When a designated keyword pattern appears | Topic steering, off-topic detection. |
| Activity-timeout | When no messages have been sent for N milliseconds | Idle prompts, participation encouragement. |
| Participant-count | When the number of active participants crosses a threshold | Reacting to departures, waiting for arrivals. |
These trigger types, combined with the action vocabulary above, are what make automated AI facilitation in Carrier expressive: a mediator can be configured to periodically summarise the discussion every two minutes, prompt any participant who has been silent for ninety seconds, or steer the conversation back on topic when a specified keyword has not appeared in the last thirty messages.
3.4.5 Activity monitoring
A mediator can optionally maintain an activity model of each communicator. The activity-monitor settings are:
- An idle threshold (milliseconds of inactivity before a participant is considered idle), and a list of idle prompts to send when it is reached.
- An active threshold (messages-per-minute rate at which a participant is considered to be dominating), and a list of active prompts to send when it is reached.
This is the building block for participation-equity interventions: a mediator that automatically prompts quiet members and gently invites dominant members to “make space for others”.
3.4.6 Research uses of the mediator role
| Design | Mediator configuration | What it studies |
|---|---|---|
| Automated facilitator | LLM mediator with periodic + topic triggers | Effectiveness of automated facilitation. |
| Turn-taking enforcement | LLM mediator using disable_chat with all_others_responded | Effects of structured discussion on quality. |
| Participation equity | LLM mediator with activity monitoring and idle prompts | Interventions on balanced participation. |
| Discussion steering | LLM mediator with topic-detected triggers | Topic-management strategies. |
| Human facilitator | Human in mediator role, broadcast capability | Expert facilitation patterns. |
| Timed interventions | Scripted mediator with periodic broadcasts | Information-injection effects. |
| Real-time summarisation | LLM mediator with aggregate triggers and a synthesis prompt | Impact of real-time summaries on deliberation. |
§3 · Roles
3.5 Processor
3.5.1 Framing
The processor is the most novel of the three roles. It operates in the input composition space rather than the message exchange space — that is, it acts before a communicator's text becomes a message in the chat. Where a mediator sits over the conversation and a communicator sits inside it, a processor sits alongside the input box, reviewing what the communicator is about to send, generating drafts on request, or offering live suggestions as the communicator types.
The role exists because the act of composing a message is a distinct site of intervention — distinct from facilitating the conversation, and distinct from participating in it. A study that wants to ask “what happens when an AI helps people write what they say?” needs a place to put that AI, and that place is not the chat.
3.5.2 The design space, in three dimensions
It is tempting to think of processors in terms of “review” and “generate” alone, but the design space is richer. Three independent dimensions structure it.
Initiation. Who starts an interaction?
- Communicator-initiated — the communicator explicitly asks (submits a draft for review, clicks Generate).
- Processor-initiated — the processor offers help without being asked (sends a suggestion).
- System-initiated — the platform triggers an interaction based on an event (a pause is detected, a timer elapses).
Control. Who controls the final output?
- Communicator retains control — the communicator always decides what is actually sent (accept / reject / edit the processor's output).
- Processor retains control — the processor decides what the communicator sees (filtering, rewriting).
- Shared control — both can edit; the final version is negotiated.
Timing. When does the interaction happen?
- Pre-send — before the message enters the chat (review, approval).
- During composition — while the communicator is typing (real-time suggestions).
- On-demand — when explicitly requested (a Generate button).
Not every combination of these dimensions is feasible. LLM API latency makes true simultaneous co-editing impractical for LLM processors; processor-controlled rewriting risks undermining the validity of self-report studies by replacing the communicator's voice with the processor's. Carrier's design therefore commits to one principle and offers three concrete modes.
The committed principle: communicator agency. Whatever the processor does, the communicator retains final control over what enters the chat. The processor never bypasses the communicator's agency.
3.5.3 The three modes
Review
Communicator writes draft → submits for review → processor gives feedback
↓
Communicator accepts / edits / rejects → message sent to chat
| Dimension | Setting |
|---|---|
| Initiation | Communicator-initiated (on-submit) or system-initiated (pause-triggered) |
| Control | Communicator |
| Timing | Pre-send |
Configuration parameters:
- Trigger — on-submit (the communicator clicks Send and the processor steps in) or pause-triggered (the processor steps in automatically after the communicator stops typing for a configured period).
- Pause timeout — the inactivity window, in milliseconds, used only when the trigger is pause-triggered.
- Feedback format — freeform (the processor writes a free-text response), inline-edit (the processor proposes an edited version of the draft), or approve-reject (the processor returns a binary verdict).
- Mandatory — whether the communicator must address the feedback before they can send the message.
- Max rounds — a cap on the number of review iterations on a single draft.
Research applications include writing-quality improvement, self-reflection, metacognitive scaffolding, and peer-review dynamics.
Generate
Communicator clicks Generate → processor creates a draft
↓
Communicator edits → message sent to chat
| Dimension | Setting |
|---|---|
| Initiation | Communicator-initiated (explicit request) |
| Control | Communicator (edits before sending) |
| Timing | On-demand |
Research applications include AI-ghostwriting perception, co-authoring dynamics, and the trade-off between generation quality and editing effort.
Real-time assist (human processors only)
Communicator types → the draft streams to a human processor
↓
Processor sends ephemeral suggestions → communicator accepts or dismisses
| Dimension | Setting |
|---|---|
| Initiation | System-initiated (continuous streaming) |
| Control | Communicator (suggestions are ephemeral) |
| Timing | During composition |
This mode is restricted to human processors because LLM round-trip latency makes truly live suggestions impractical. The use cases — peer coaching, expertise-based assistance, live mentoring during composition — all assume a human partner at the other end.
3.5.4 Phase scripts: dynamic processor behaviour
A processor does not have to behave the same way throughout a chamber. Carrier supports phase scripts — an ordered list of phases, each with a mode and a transition trigger that advances to the next phase.
phases: [
{ id: "warmup", mode: "disabled", transition: { on-start } }
{ id: "review", mode: "review", transition: { message-count: 5 } }
{ id: "generate", mode: "generate", transition: { time-elapsed: 180000 } }
]
The processor in this example starts disabled, switches into review mode once five messages have been exchanged, and switches into generate mode three minutes after the segment starts. The supported transition trigger types are:
| Trigger | When the next phase activates |
|---|---|
| On-start | Immediately when the segment begins. |
| Message-count | After N messages have been sent in the chatroom. |
| Time-elapsed | After N milliseconds from segment start. |
| Keyword | When a designated keyword appears in the chat. |
| Participant-event | When a participant joins or leaves. |
| Manual | When the experimenter advances from the dashboard. |
| On-end | When the segment ends. |
Phase scripts are how Carrier supports designs like progressive scaffolding (start with heavy review, then withdraw assistance over time) or adaptive intervention (switch modes once a quality threshold has been crossed).
3.5.5 Context configuration
The amount of conversation a processor can see is configurable:
| Level | What the processor sees |
|---|---|
| None | Only the current draft; no chat history. |
| Partial | The last N messages (configurable). |
| Full | The complete chat history of the chamber so far. |
For LLM processors, the context level controls what is sent to the model with each call. For human processors, it controls the contents of a read-only chat panel beside the suggestion interface. Context level interacts with privacy considerations: a research design that does not want the processor influenced by — or able to see — earlier conversation should set context to none.
3.5.6 Research uses of the processor role
| Design | Processor configuration | What it studies |
|---|---|---|
| AI writing coach | LLM processor, review mode, freeform feedback | Improvement of writing quality through AI feedback. |
| Mandatory review | LLM processor, review mode, mandatory: true | Effects of forced reflection on output. |
| AI ghostwriter | LLM processor, generate mode | Authorship perception, AI-assisted communication. |
| Peer review | Human processor, review mode | Peer-feedback dynamics. |
| Live coaching | Human processor, real-time assist mode | Expert scaffolding during composition. |
| Progressive scaffolding | Phase script: disabled → review → generate | Effects of withdrawing assistance over time. |
| Mode comparison | Two chamberlines: one review, one generate | Trade-off between reviewing and generating. |
§3 · Roles
3.6 How the roles interact
The three roles are designed to be orthogonal — to operate in different spatial zones, on different inputs, with different output channels — but they can be present in the same chamber and combined in principled ways.
Communicator ↔ mediator. Asymmetric. The mediator sees everything; the communicator does not see the mediator's view. The mediator can broadcast to communicators, disable or enable their input, prompt them privately, and highlight their messages. This asymmetry is intentional: it models the moderator dynamic.
Communicator ↔ processor. Collaborative but communicator-controlled. The processor sees drafts; it sends feedback, suggestions, or generated text; the communicator decides what actually gets sent. The processor never bypasses the communicator.
Mediator ↔ processor. Orthogonal. Mediators act in the broadcast/control layer; processors act in the composition layer. They do not directly interact, but their effects can be coordinated — a mediator might broadcast that “the next message will be reviewed” at the same trigger point at which a processor switches from disabled to review mode.
Four representative configurations to keep in mind:
| Configuration | Roles present | Research scenario |
|---|---|---|
| Communicators only | 2+ communicators | Standard group discussion. |
| Communicators + mediator | N communicators + 1 mediator | Facilitated discussion. |
| Communicators + processor | N communicators + 1 processor | Assisted composition. |
| Full setup | N communicators + mediator + processor | Facilitated discussion with composition assistance. |
§3 · Roles
3.7 Design principles, distilled
Six principles run through the role system. They are summarised here for reference.
- Separation of concerns. Each role occupies a distinct spatial zone (chat, broadcast, input) and serves a distinct function (communicate, facilitate, assist). The separation is what enables clean experimental contrasts.
- Communicator agency. The communicator always retains final control over what they say. No mediator or processor can speak in their name.
- Type orthogonality. Any participant type that is compatible with a role can fill it. This is what makes human–AI comparisons trivial to set up.
- Phase-based dynamism. Both mediators (via triggers) and processors (via phase scripts) can change behaviour during a session, enabling within-session manipulations and progressive designs.
- Comprehensive logging. All messages, broadcasts, mediator actions, and processor interactions are recorded with metadata. This is what makes the resulting data analysable.
- Configuration hierarchy. Global → chamber → segment overrides → event-based control. Defaults at the top, specificity at the bottom.
§3 · Roles
3.8 Builder walkthroughs
The researcher clicks “Create New Agent”, names it Facilitator, expands the LLM Response section and picks a provider, a model and a system prompt, then confirms with “Add Agent”. The agent joins the Library. They open the chamber and drag it into the Mediators lane — a role is not a field on the agent, it is the lane you drop it into. (Triggers are per-chamber and are authored afterwards in the Script Builder; see §5.7.)
The researcher creates an agent called Draft Reviewer, selects it in the Library and switches its Role to Processor — which replaces the trigger-script half of its configuration with the phase editor. They click “Add Phase” three times; each phase carries its own mode (review, generate, real-time-assist or disabled), its own transition trigger, context level, feedback format and AI settings.
Variables — the attributes that travel with each participant and decide which slots they can fill, which chambers they see, and what each activity says to them — are the subject of §4.
Part I · §4. Variables
Variables are how a Carrier experiment becomes personal. They constrain matching, gate visibility, inject context into prompts, and drive triggers.
§4 · Variables
4.1 Why variables exist
By the end of Chapter 2 we have a design that can place a researcher's chosen mixture of humans, LLM chatbots, scripted chatbots, and agents inside a sequence of chambers, with each role doing what the design expects. That design is, however, still impersonal: it treats every participant identically. The same chamberline, the same slots, the same instructions, the same prompts — applied to every person who walks into the experiment.
Variables are how a Carrier experiment becomes personal. A variable is an attribute attached to a participant — their self-identified expertise, the political party they support, the score they obtained on the pre-survey, the option they ranked first in the last chamber — that is then available to the rest of the experiment to consult. Variables let you:
- Constrain matching. Require that the matched group contain (say) one self-identified novice and one self-identified expert.
- Gate visibility. Show a remedial chamber only to participants whose pre-test score was below threshold; skip the chamber for everyone else.
- Inject context into instructions and prompts. Address each participant by their chosen name; brief an LLM mediator on the political identification of each person in the room.
- Drive triggers (§5). Fire a bot's response only when the variable matches a condition.
In conventional methodological language, variables are how Carrier represents the experimentally-relevant measured and manipulated characteristics of each participant. Once captured, they travel with the participant for the rest of the run.
§4 · Variables
4.2 The anatomy of a variable
A variable is defined once, at the experiment level, and then referenced from anywhere in the experiment. Each definition has the following parts:
| Part | What it specifies |
|---|---|
| Key | The short identifier used to reference the variable (e.g. expertise, party_id, pretest_score). |
| Type | The kind of value the variable can take: string, number, boolean, single-choice, multi-choice. |
| Source | Where the value comes from — a survey response, the result of a segment, a system-assigned value, or an aggregate over other participants. |
| Options (for choice types) | The list of allowable values, optionally each with a display label and a numeric value (see 3.2.2). |
| Default | The value used when the source produces nothing. |
Variables are addressed in the rest of the experiment by their key, prefixed with
var. — for example, var.expertise or
var.party_id. The prefix exists so that the system can tell, when
reading a configuration, that a string is referring to a variable rather than to a
literal.
4.2.1 Where variable values come from
A variable's value is computed from a source specification. The five common sources are:
- Survey response. The most common source. Read the value of a named question from one of the participant's completed surveys — the global pre-survey, a chamber pre- or post-survey, or any embedded
surveysegment. Survey-based variables are evaluated as soon as the survey is submitted, which means they are available for chamberline assignment, slot matching, and chamber visibility from that point forward. - Segment submission. Read what the participant submitted during a particular segment — the option they chose in a
selectionsegment, the order they produced in arankingsegment, the text they entered in aninputsegment. A submission from a segment embedded in a chat is available during that chat (the embedded card runs alongside it): triggers and prompts in the parent chat can read it, but the value is empty until the participant actually submits, and the builder's pickers flag such variables as “available during segment”. - Segment data. Read data captured about a segment — how long the participant spent, how many messages they sent, whether they passed an attention check.
- System-assigned. Read a value the platform sets automatically — the chamberline the participant was assigned to, their participant ID, the run identifier.
- Aggregate. Compute a value across multiple participants — the mean of all communicators' pre-test scores in the same chamber, for example, or the modal political identification of the group. Aggregates are how a configuration can talk about the group, not just the individual.
A source can be qualified by a participant reference: by default a variable is read
from the participant who is being configured (referred to internally as self),
but it can also be read from another slot in the chamber (slot:1,
slot:2, or slot:mediator). This is what makes it possible
to address a fellow participant in an instruction or in a system prompt — for
example: “you are about to chat with
{slot:1:var.first_name}, who identifies as a
{slot:1:var.party_id} voter.”
4.2.2 Per-option numeric values
A choice-type variable is, by default, a categorical label — “novice” or “expert”, “Democrat” or “Republican”. When a numerical contrast is also useful, each option in the variable's definition can carry an associated numeric value.
A typical case is a five-point self-rating scale. The variable's options might be labelled Very low / Low / Moderate / High / Very high, with numeric values 1 / 2 / 3 / 4 / 5. The label is what the participant sees and what is logged in raw form; the numeric value is what the configuration arithmetic uses when computing aggregates or threshold conditions. A condition like “this chamber only appears if the participant's expertise is ≥ 3” is expressed against the numeric value, not the label.
Numeric values are optional. A variable without them behaves as a pure categorical attribute.
4.2.3 Aggregate variables across non-human participants
When an aggregate variable is computed across the participants of a chamber, the researcher can choose whether non-human participants (LLM chatbots, scripted chatbots, and agents) are included or excluded from the aggregate. This setting matters in two common situations:
- Confederated designs (one human, several non-human communicators) — usually the researcher wants aggregates to reflect humans only, since the non-humans are confederates.
- Group-property analyses (e.g. mean expertise of the room) — the researcher chooses whether non-human participants carry an expertise value at all, and whether it contributes to the mean.
The default is to include only humans in aggregates; the alternative is configured on each aggregate variable definition. The same humans-only / include-all toggle applies regardless of which of the three non-human kinds is present in the chamber.
§4 · Variables
4.3 Variables as matching constraints
The first place a variable does work is during matching. There are three places in the configuration where a variable can constrain who is matched into what:
4.3.1 Chamberline eligibility
A chamberline can declare a filter — a condition expressed against variables that a participant must satisfy in order to be eligible for that chamberline. When the experiment's chamberline-assignment method is survey-based (see §1.2.2), the filter is the mechanism by which the assignment is made:
Chamberline "Pro-disclosure" filter: var.party_id == "Democrat"
Chamberline "Anti-disclosure" filter: var.party_id == "Republican"
A participant whose var.party_id is Democrat will be eligible
for the first chamberline and not the second; the inverse holds for
Republican participants. Participants who do not satisfy any chamberline
filter are routed to a default chamberline if one exists, or terminated gracefully
otherwise.
4.3.2 Slot requirements
Inside a chamber, each slot can declare a list of required properties — conditions that the participant filling the slot must satisfy. A two-slot chamber with the slot constraints
Slot 1 human communicator requires: var.expertise <= 2
Slot 2 human communicator requires: var.expertise >= 4
will pair each session into one novice and one expert. Matching does not begin for this chamber until two participants are waiting who jointly satisfy the slot constraints — a queued participant whose expertise is 2 can be matched into Slot 1, and a queued participant whose expertise is 5 can be matched into Slot 2, but two novices cannot fill the chamber on their own.
Slot requirements can reference any variable that has been resolved by the time the chamber begins. In practice this means anything captured in the global pre-survey, anything assigned by the system, and anything from earlier chambers in the participant's run.
4.3.3 Building one: a variable that assigns the pairing
The whole journey, in the builder, end to end. A counterbalance variable with two values is defined once, and each of the chamber’s two human slots then requires one of them. From that point matching will only seat a participant where their value fits, so every pair is one of each — and because the variable is counterbalance rather than random-pick, the draw leans toward whichever side is short.
e2e/matching-variable.spec.js. The test drives a real
browser against a real server and database and then checks what reached the
API, so this recording cannot show a walkthrough that no longer works.
Three things in that recording are worth naming. The kind picker is where the eight kinds are chosen, and each card carries the one-line description of what that kind does — the documentation sits on the control rather than in a panel beside it. The slot’s Required Properties key field is a picker of the variables that resolve at that position, not a free-text box; a variable that is not yet available there is listed with the reason. And once the slots are wired, the variable’s own Used in panel reports gates 2 match slots in 1 chamber, so the dependency is visible from both ends.
To run it yourself: npm run demo:matching seeds the fixture and
starts the app; npx playwright test e2e/matching-variable.spec.js
walks it in a browser, checks what reached the database, and rebuilds the
clip above.
4.3.4 Group-composition constraints
Slot-by-slot constraints are sometimes too weak to express a desired group property. Where the slot constraints care only about who fills each seat, a group-composition constraint cares about a property of the matched set as a whole. Typical examples:
- The chamber requires a gender-balanced trio — at least one male, at least one female, no constraint on the third seat.
- The chamber requires that the modal political identification of the trio is Democrat.
- The chamber requires that no two members share the same self-reported expertise level.
Group-composition constraints are expressed against aggregate variables (3.2.3) and are evaluated against the candidate set during matching. A candidate matching is admitted only if the corresponding aggregate, computed over the candidates, satisfies the constraint.
§4 · Variables
4.4 Variables as visibility gates
A second use of variables is to gate what a participant sees, not just who they see it with. A chamber can declare a visibility condition against one or more variables. The chamber is included in the participant's run only if the condition is true at the moment the chamberline would otherwise enter that chamber.
This is the canonical mechanism for branching designs:
- A practice chamber that is shown only to participants whose pre-test score was below threshold.
- A debriefing chamber for the high-anonymity condition that does not exist in the low-anonymity condition (an alternative to two parallel chamberlines, useful when most of the experiment is identical between conditions).
- A post-task survey that is shown only to participants who actually completed the preceding chat (skipped for those whose chamber was terminated early).
Visibility conditions take the same form as slot constraints: a boolean expression
against variable values, with the standard comparison operators and
any / all connectors. Crucially, visibility is evaluated
just before the chamber would begin, not once at the start of the run.
This means that variables produced during the run — by an earlier
chamber's segment submission, by a chamber post-survey — are usable as
gating conditions for later chambers.
When a chamber is gated out for a participant, the participant skips directly to the next chamber in their chamberline. The chamberline itself does not change.
4.4.1 Slot requirements vs visibility conditions
A natural question at this point: a chamber whose every slot has a
requires constraint already excludes participants who cannot fill any
seat — they will never be matched. Why, then, does Carrier also offer a
separate visibility condition at the chamber level? Don't they do
the same job?
They overlap in one specific case and diverge in the others. The rule that governs the overlap is:
Implicit skip rule. If every human slot in a chamber has a non-empty
requiresconstraint, and the participant satisfies no slot's constraint, then the chamber is silently skipped for that participant — exactly as if it had a failing visibility condition.
The skip is implicit: there is no separate visibility expression to write. The chamber is treated as not part of the participant's run, and they advance to the next chamber. This is what you observed.
Three scenarios make the distinction concrete.
Scenario A — Single fully-constrained slot. A
solo-participant chamber whose one human slot requires
var.condition == "treatment". A control-condition participant cannot
fill it; the implicit skip rule fires; the participant moves to the next chamber.
Here, slot requirements alone are sufficient — adding a chamber-level
visibility condition would be redundant.
Scenario B — Mixed open and constrained slots. A two-slot
chamber where one slot requires var.role == "expert" and the other
slot has no constraint:
Slot 1 human communicator requires: var.role == "expert"
Slot 2 human communicator requires: (none)
A novice participant can fill Slot 2, so the implicit skip rule does
not fire — the chamber is visible to them, and they will
join it paired with an expert. If you intended this — “novices and
experts meet here, with the expert always seated in Slot 1” — that
is the correct behaviour and you should add nothing further. But if you intended
the chamber to exist only for experts (with Slot 2 reserved for another expert
who happens to be unconstrained because of how you defined the slot), the implicit
skip rule will not save you. A novice will silently enter. To get a true gate here,
add a chamber-level visibility condition var.role == "expert". Slot
requirements cannot express this on their own.
Scenario C — Chamber-level gate plus slot-level role assignment. A debriefing chamber that should exist only for participants who completed a treatment chamber upstream, and within that chamber should pair a “writer” participant with a “reviewer” participant:
Chamber "Debrief"
visible if: var.completed_treatment == true
Slot 1 human communicator requires: var.debrief_role == "writer"
Slot 2 human communicator requires: var.debrief_role == "reviewer"
Both layers are necessary. The chamber-level visibility expresses who belongs
in this chamber at all; the slot requirements express which seat each
participant takes once they are here. Trying to collapse the gate into a slot
constraint (for example, by adding var.completed_treatment == true to
both slots' requires) works only by coincidence — the implicit
skip rule fires because every slot is constrained — and it conflates
two distinct intents. If a future edit relaxes Slot 2's requires
to allow walk-ins, the chamber suddenly becomes visible to participants who never
completed the treatment, and the gate is silently gone.
This last point is the deeper reason the two systems are kept separate. Visibility conditions express intent explicitly; slot requirements express it emergently. When the only way a chamber is skipped is “all slots happen to be constrained and the participant happens to satisfy none,” that skip is a side effect of a configuration that was written for a different reason. Edits to the slots — adding a slot, opening one up, retitling roles — can quietly remove the gate. An explicit visibility condition survives those edits and is auditable as a routing rule on its own.
A compact way to choose between them:
| You want… | Reach for |
|---|---|
| A chamber that exists only when seat-level requirements alone suffice to exclude the wrong participants, and you do not anticipate slot edits relaxing this. | Slot requirements alone. |
| A chamber that should be hidden for some participants even though other participants would still find an open seat in it. | A chamber visibility condition. |
| A chamber that gates a population and role-assigns within that population. | Both: visibility condition for the gate, slot requirements for the seating. |
| A routing rule that should be self-documenting and resilient to slot edits. | A chamber visibility condition (in addition to whatever slot requirements you also want). |
In short: when slot requirements happen to act as a gate, treat that as a convenient side effect, not as the gate itself. If the chamber is meant to be conditional on a participant property, say so with a visibility condition.
§4 · Variables
4.5 Variables in instructions and prompts
The third use of variables is the most pervasive: interpolation into the natural-language content shown to or used by participants. Any place in the experiment where text is shown to a participant or sent to an AI is also a place where variables can be injected.
The interpolation syntax is {{var.<key>}} for a variable read
from the current participant, and {{slot:<n>:var.<key>}} or
{{slot:<role>:var.<key>}} for a variable read from a fellow
slot. A few worked examples:
Instruction segment text
Welcome, {{var.first_name}}. In the next ten minutes you will discuss
climate policy with two other participants. The participant on your
left has expertise level "{{slot:1:var.expertise_label}}"; the
participant on your right has expertise level
"{{slot:2:var.expertise_label}}".
System prompt for an LLM mediator
You are facilitating a discussion among three participants. Their
self-identified political positions are: {{slot:1:var.party_label}},
{{slot:2:var.party_label}}, and {{slot:3:var.party_label}}. Adjust
your tone to be welcoming to all three positions; do not take sides.
Survey question stem
Earlier you said your most important consideration was
"{{var.top_value}}". On the slider below, indicate how strongly you
still feel that this is your most important consideration.
Two things to note. First, interpolation happens at the moment the text is
needed — the same instruction segment used in a chamber that appears
twice will be re-interpolated each time, with the latest variable values. Second,
an undefined variable interpolates to the empty string by default, but the
variable definition's default field can be used to specify a fallback.
Where it matters, configure a default.
4.5.1 Per-option text vs per-option value in interpolation
Recall that a choice-type variable can carry both a label and a numeric value for each option (3.2.2). When interpolating into text, two conventions matter:
{{var.expertise}}interpolates the key of the chosen option (“low”, “moderate”, “high”).{{var.expertise_label}}interpolates the display label of the chosen option (“Low”, “Moderate”, “High”).{{var.expertise_value}}interpolates the numeric value of the chosen option (e.g. 2, 3, 4).
For a five-point Likert variable, the three forms give you the same information at three different levels of formality. Choose by context: instructions to participants benefit from the display label; an LLM system prompt typically benefits from the numeric value or the key.
§4 · Variables
4.6 Variables in trigger conditions
A fourth use of variables is in trigger conditions
(§5): a bot's keyword trigger, time
trigger, or message-count trigger can be qualified by a check on a variable. This
makes triggers conditional on participant attributes — a bot might only
respond to keyword “X” if slot:1:var.condition is
“treatment”, for example. The full mechanics of trigger conditions are
covered in §5.2; here it is enough to know
that the same var.<key> references work inside trigger
conditions as work elsewhere.
§4 · Variables
4.7 Builder walkthroughs
The researcher opens Variables, clicks “+ Add variable”, and gives it the key expertise and a description. They set Kind to alias — the kind that reads its value from somewhere else in the study — set the data type to number, and use “Pick source…” to point it at a submission source. “Apply Changes” commits it, and reopening the manager shows expertise in Experiment variables. Variables are one of eight kinds (constant, alias, aggregate, concat, expression, random-pick, counterbalance, llm-processed); there is no “single-choice” kind, and a variable does not generate _label / _value companions.
The researcher drags two human participants into a chamber’s Communicators lane, then clicks the first slot and adds a required property under Required properties (matching): var.expertise ≤ 2. They click the second slot and add var.expertise ≥ 4. Only participants whose properties satisfy all of a slot’s conditions can fill it, so the chamber now pairs a novice with an expert.
The researcher selects a chamber, renames it “Remedial Explainer”, opens its Visibility Conditions panel and clicks “Add Condition”. They enter the key var.pretest_score, choose the < operator and the value 0.5. Only participants satisfying every condition on the chamber will see it; leaving the list empty shows it to everyone.
Triggers — the rule system that drives the behaviour of every non-human participant in the experiment, the final piece of the four-system picture — are the subject of §5.
Part I · §5. Triggers
The rule system that drives every non-human participant's behaviour. A trigger combines a condition, a response, and optionally an action.
§5 · Triggers
5.1 Why triggers exist
The previous three chapters answered the questions what shape is the experiment, who is in it, and what do we know about them. They left one question open: how do the non-human participants behave? A scripted chatbot, an LLM chatbot, and an agent are all just slots in a chamber until something tells them when to speak and what to say.
That “something” is the trigger system. A trigger is a rule that combines:
- A condition — what has to be true before the trigger fires.
- A response — what the bot says if the trigger fires.
- Optionally, an action — what else the bot does if the trigger fires (disable a participant's input, prompt a specific person, transition the chamber, and so on).
You can think of a non-human participant's configuration as a book of rules: an ordered list of triggers, each with a condition–response–action triple. When something happens in the chamber, Carrier consults the book in order, evaluates the conditions against the current state of the chat, and fires the rules that match. This applies equally to deterministic scripted chatbots (whose responses are pre-written sentences), to LLM chatbots (whose responses are generated by a language model on the fly), and to agents (whose responses are generated by a language model that has consulted its tools first).
A useful analogy from the methods literature: a trigger is to an interactive bot what a confederate script is to a confederate participant — a list of contingent rules that say “when X happens, do Y”. Carrier's contribution is to make the list expressive enough to capture the contingencies real conversations contain, and machine-readable enough to be replayed identically across sessions.
§5 · Triggers
5.2 The trigger model: condition → response → action
A single trigger has the structure:
The next three sections expand each of these three parts. The modifiers are covered together in §5.6.
§5 · Triggers
5.3 Conditions: when a trigger fires
The condition of a trigger is the part most worth understanding well, because it determines whether the rule ever runs. Carrier supports a catalogue of condition types organised by what they listen for: message content, time, message counts, sequences, participant events, and aggregate states.
5.3.1 The catalogue of condition types
| Type | Listens for | Typical use |
|---|---|---|
keyword | A configurable word or phrase in a chat message | Respond when “climate” is mentioned; greet on “hello”. |
regex | A regular expression match in a chat message | Recognise URLs, profanity, structured statements like “I disagree with X”. |
time | A delay (ms) from chamber or segment start | Send an opening prompt 5 s in; remind participants of the time after 8 min. |
message-count | A total count of messages in the chatroom | Intervene every N messages; introduce a summarisation at message 20. |
participant-message-count | A count of messages from a specific participant | Detect that one person has dominated; reward an under-contributing participant. |
sequence | An ordered series of keyword matches | “First X is said, then Y” — useful for staged conversation steering. |
participant-action | A participant event such as join, leave, idle | Greet on join; flag a drop-out; chain into a backup-bot prompt. |
after-bot-message | A specified bot has just sent a message | Cross-bot chaining; staged multi-bot interactions. |
event-monitor | An arbitrary chatroom event | Catch dashboard interventions, segment transitions, embedded-child completions. |
chain-only | (Passive) Only fires when another trigger chains to it | Building multi-step responses. |
llm-driven | The trigger asks an LLM to decide whether it should fire and what to say | Open-ended judgement triggers — see §5.4.2. |
periodic | (Mediator only) Fires at a fixed interval | Regular check-ins, repeated summaries. |
aggregate | (Mediator only) Fires after N messages have accumulated within a time window | Batched synthesis. |
topic-detected | (Mediator only) Fires when a topic pattern is detected | Topic steering. |
activity-timeout | (Mediator only) Fires after a period of inactivity | Idle prompting. |
participant-count | (Mediator only) Fires when the active participant count crosses a threshold | Reacting to departures or arrivals. |
discussion-phase | (Mediator only) Fires at the start, middle, or end of a chamber | Phase-appropriate facilitation. |
Two practical notes:
- Most of these conditions presuppose a chat segment. Keyword, regex, message-count, sequence, and the mediator-only message conditions all listen to the conversation; if the chamber is currently in a slide or a survey segment, they are silent. Time and participant-action conditions, by contrast, can fire from any segment.
- The mediator-only types (
periodic,aggregate,topic-detected,activity-timeout,participant-count,discussion-phase) are enforced as mediator-only by both the builder and the runtime. The builder hides them from non-mediator agents' trigger pickers, and the runtime only initialises and evaluates them for bots withrole === 'mediator'. Attaching one of these types to a communicator or processor (e.g., by hand-editing the JSON) will silently never fire.
5.3.2 Modifiers shared by all condition types
Every trigger, whatever its condition type, can be qualified by a small set of modifiers:
| Modifier | Meaning |
|---|---|
| Case sensitivity | For keyword and regex conditions, whether matching is case-sensitive (default: insensitive). |
| Match mode | For multi-value conditions, whether any of the values is sufficient (the default) or all are required. |
| Sender filter | Restrict matching to messages sent by humans, by a specific participant, or by a participant of a specific role. The default is to consider every sender. |
| Variable filter | A boolean expression over var.* references; the trigger does not fire unless the expression is satisfied. This is how a trigger becomes condition-dependent — for example, “only fire this prompt if the current speaker's var.condition == 'treatment'”. |
The variable filter is especially powerful in combination with §4: a single set of triggers, attached to a single bot template, can produce qualitatively different behaviours in different chamberlines purely on the basis of the variables that are true of the participants in front of it.
5.3.3 An aside: triggers, segments, and the “active segment” filter
Most trigger configurations include an active-segments filter — a list of segment IDs (within the chamber) during which the trigger is eligible to fire. Leaving the list empty means fire in any segment. Restricting it to a specific segment — typically a chat segment — is the canonical way of writing “this rule only applies during the deliberation, not during the survey at the end”. The filter also accepts embedded child segments, which is how a trigger can be made to fire only while a particular embedded vote overlay is open.
This is a small detail, but it is the source of a very common pitfall: a trigger that “doesn't seem to fire” is often a trigger whose active-segment filter excludes the segment the chamber is actually in.
The filter has a second consequence that matters more than it looks: a time trigger's clock restarts at every segment entry. There is no chamber-wide clock. When the chamber moves into a segment, Carrier arms every time trigger scoped to that segment from zero; when the segment ends, any timer that has not yet fired is dropped. Three things follow, and each of them has bitten somebody:
- A delay longer than the segment lasts never fires. A trigger set to 12:00 in a segment that auto-advances at 10:00 arms, waits, and is discarded — silently, at run time.
- A trigger with an empty active-segments list is armed in every compatible segment, and its countdown restarts in each one. “Fire once at 30 seconds” becomes “fire at 30 seconds into each of three segments” unless Max triggers says otherwise.
- Two delays are only comparable within one segment. Ten seconds into a late segment happens after forty-five seconds into an early one, however the two numbers sort.
Idle detection keeps a different clock again: its value is measured from the last message, not from segment entry, so it is the earliest the trigger can fire rather than when it will — and in a busy chat it may never fire at all. The Timeline view (§5.6.2) draws all of this, and reports the cases that cannot work.
§5 · Triggers
5.4 Responses: what the bot says
When a trigger's condition fires, the response tells Carrier what message to send. There are two fundamentally different kinds of response.
5.4.1 Scripted responses
A scripted response is a pre-written sentence (or a list of sentences from which Carrier picks one at random). It is the only kind of response a scripted chatbot can produce, and it is also available to LLM chatbots and agents for cases in which the researcher wants exact-text control.
Configuration is minimal:
- Message — a single fixed string, or a list from which the platform draws (uniformly, by default).
- Delay — an interval before the message is actually sent, simulating typing or thought.
- Probability — the chance that the trigger fires at all when its condition is satisfied (default 1.0). Used when researcher wants stochastic intervention.
Scripted responses are deterministic: identical inputs produce identical outputs. Two participants who encounter the same conversation in the same condition will receive the same scripted response, with the same delay, in the same order. This makes them the right choice for studies in which experimental control matters more than naturalism — confederated-AI conformity studies, attention-check probes, scripted “noise” injections, and so on.
5.4.2 LLM-generated responses
The alternative is to let an LLM generate the response on the fly. A language-model response is produced by sending a request to a language-model provider (OpenAI, Anthropic, Google, or a compatible service) with a prompt assembled from:
- The system prompt of the bot (its personality, role, instructions).
- A configurable amount of chat history as context (none, the last N messages, or the full history — see §3.5.5 for the analogous setting on processors).
- Variable interpolations (§4.5) — the participant's name, condition assignment, current state.
- Optional chain steps — a sequence of LLM calls in which each step's output becomes part of the next step's input. Chains are how a single trigger can implement plan, then critique, then rewrite behaviour.
The response of an LLM-driven trigger is therefore open-ended: the model can produce any text that satisfies its prompt, modified each time by the current state of the conversation. Where scripted responses trade variation for control, LLM responses trade control for naturalism. They are the right choice for studies in which the realism of the bot's behaviour is itself part of what is being tested.
A note on the response format: Carrier's LLM responses follow a structured JSON
shape with three fields — content (the message text, or
null to remain silent), rationale (a brief justification,
logged but not shown), and an optional actions list (the topic of
§5.5). The structure exists so that the AI
can decide “remain silent” without producing a blank message, and so
that researchers can audit why the AI chose to speak after the fact.
5.4.3 Choosing between scripted and LLM responses
A short heuristic that holds in most studies:
| If you want… | Prefer… | Because… |
|---|---|---|
| Exact replicability across sessions | Scripted | Same input → same output, always. |
| Naturalistic, varied bot behaviour | LLM | The model adapts to what the participant actually said. |
| A confederate that does not break character | Scripted | LLMs can drift; scripted text cannot. |
| A facilitator that responds to topics in real conversation | LLM | Researcher cannot pre-write every possible response. |
| Strict auditing of bot speech | Scripted | The set of possible utterances is finite and visible. |
The two are not mutually exclusive on a single bot. A bot template can mix scripted triggers (for greetings, attention checks, and exit messages) with LLM triggers (for the bulk of the discussion), routing each contingency through whichever response type fits best.
5.4.4 Response logic and silence
For LLM chatbots and agents, two additional configuration parameters govern when not to speak. They sit alongside the trigger list and are sometimes more important than it:
triggerOnFirstMessage— whether the bot is allowed to make the first move (greet, open the discussion) before any human has spoken.respondToEveryMessage— whether the bot should attempt to respond after every incoming message, or only when one of its triggers explicitly says so.respondOnMention/mentionKeywords— whether the bot only responds when its name (or a configured list of keywords) appears in the chat.initialSalute— a configured opening message sent on chamber start, regardless of triggers.timeoutTrigger— a “fail-safe” prompt the bot sends after a configured silence interval if no other trigger has fired.
These five toggles, together with the trigger list, are how a researcher tells an LLM chatbot or agent the difference between a talkative configuration (“respond whenever spoken to, and also if no one has spoken for a minute”) and a reserved one (“only respond when explicitly addressed by name”).
§5 · Triggers
5.5 Actions: what else the bot does
A trigger's action field — present on every trigger, optional in
most cases, central for mediators — is the side effect that fires alongside
(or instead of) a response message. Actions are what allow a bot to do something
to the chat rather than just in the chat.
Carrier exposes actions through four distinct mechanisms, which are easy to confuse but do different things. Read this list once before the subsections below:
| Mechanism | Available to | What it does | Decided when? |
|---|---|---|---|
| Scripted actions | Scripted chatbots, and any LLM bot whose trigger has a baked-in action | Apply a Carrier intervention (disable chat, prompt, highlight, …) with parameters fixed in the trigger configuration | At configuration time |
| LLM-chosen Carrier actions | LLM chatbots and agents acting in a mediator role | The model emits a structured response whose actions field selects which interventions to fire and with what parameters | At each turn, by the model |
| Segment-submission actions | All non-human participants (scripted or LLM) configured as communicators in an interactive segment | Submit a vote, a ranking, or a free-text answer alongside humans — optionally counting toward the segment's completion | At configuration time, with the submitted value optionally resolved per session |
| Agent built-in tools | Agents only (Claude Agent API) | The Claude Agent autonomously reads files, runs commands, or browses the web to gather information for itself before producing its message | Across multiple internal steps within a single turn, by the agent |
The first two are about the bot acting on the chamber — they affect what participants see and what they are allowed to do. The third is about the bot acting as a participant within an interactive segment — its submission joins the humans' in the segment's results. The fourth is about the agent informing itself — it affects what the agent knows when it speaks, but its only externally visible output is still the eventual chat message. Sections 4.5.1 to 4.5.4 cover each in turn; 4.5.5 helps choose among them.
5.5.1 Scripted actions
A scripted action is an unambiguous instruction baked into the trigger configuration: “when this rule fires, disable participant X's input until either 60 s have passed or all other participants have responded, whichever comes first.” The action is part of the rule. The bot's job is to fire the trigger; the action's effect on the chat is deterministic, predictable, and visible in the configuration before the experiment runs.
Carrier's primary catalogue of scripted-action types — drawn from §3.4.3 — is:
disable_chat— temporarily prevent a participant from sending messages, with a composite release condition.enable_chat— explicitly re-enable input.prompt_participant— send a private prompt visible only to a specific participant.highlight_message— visually highlight a past message for a configurable duration.request_attention— trigger a visual or auditory cue at a specific participant.
There are also a small number of chamber-level scripted actions used less frequently:
advance_segment— force the chamber to move to its next segment immediately.terminate_chamber— end the chamber early.set_variable— set a variable on a participant or on the chamber, useful for cascading state into later chambers' visibility conditions (§4.4).
Scripted actions are how a confederated-chatbot conformity study can be made literally identical across participants: the rules say “disable the human's input for 30 s after the first confederate's message” or “highlight the confederate's response in green for 5 s”, and the chat then unfolds with the same scaffolding in every session.
5.5.2 LLM-chosen Carrier actions
The alternative — and the more powerful in open-ended designs — is to
let the language model itself decide which Carrier interventions to fire. This is
the mechanism by which an LLM-driven mediator can act contextually: if the
model judges that one participant has been quiet for too long, it can choose to
issue a prompt_participant; if it judges that the conversation has
drifted off-topic, it can choose to send a styled broadcast.
The rule does not pre-specify the action's parameters. Instead, the trigger fires an LLM call (with the bot's system prompt and chat context), and the model returns a structured response of the form
{
"content": "<broadcast text, or null>",
"rationale": "<one-sentence justification, logged>",
"actions": [ { "type": "prompt_participant",
"target": "slot:2",
"message": "What do you think?" },
{ "type": "disable_chat",
"target": "slot:1",
"release_conditions": { ... } } ]
}
The model's selection of actions is constrained by the bot's configured action
vocabulary (researchers can choose to expose only a subset of action types to the
model) and by the bot's role: a communicator's action vocabulary is small (no
disable_chat); a mediator's is large. This mechanism is available to
any LLM-driven mediator — both LLM chatbots and agents acting in a mediator
role.
This is the most expressive — and also the least replicable — corner of Carrier intervention. The trade-off is real: an LLM-chosen action set gives you a facilitator that adapts, but the cost is that two sessions of the same condition may diverge in their facilitation. The right choice depends on what the experiment is testing.
5.5.3 Segment-submission actions
The interactive segments introduced in §2.2
— selection, ranking, and input —
collect an answer from every participant in the chamber. A non-human participant
configured as a communicator can be set up to submit alongside the humans,
the way a confederate in a behavioural lab study would. The submission is attached
to the bot's participant identity, surfaces in the segment's results, and appears
in the exported data with the same shape as a human's answer.
What the bot submits depends on the segment type. For a selection, it
is one or more option indices (or, in slider mode, a numeric value within the
slider range). For a ranking, it is a permutation of the item indices.
For an input, it is a string — or a number, for the numeric
input subtype. In every case the submission shape mirrors the human's, so
cross-participant aggregates and downstream variable expressions
(§4) read bot and human submissions
uniformly.
Where the value itself comes from is configured per trigger. Carrier supports four data modes:
- Static — the researcher hardcodes the value. The bot submits exactly that, every session. Useful when the value is the manipulation.
- Random — the value is drawn from a configured pool, optionally weighted. For
selectionandrankingthe pool is the segment's own options; forinputit is a researcher-provided list of candidate strings, since “random text” without an anchor is not meaningful. - Referenced — the value is derived from what humans have already submitted in the same segment. Strategies include match the first human's answer, match the majority option, oppose the majority, and pick a different option at random. For
selectionandrankingall four strategies translate naturally; forinput, only verbatim copy of a target human's text is well-defined. - LLM-generated — the bot calls the language model with the segment's prompt, the chamber's chat context, and a JSON-shaped schema instruction. The model returns a structured submission (an index, a permutation, or a string). This is the most flexible mode and the one most often appropriate for
input.
Three submission-metadata flags refine how the bot's answer is treated by the rest
of the chamber. countTowardTotal decides whether the bot's submission
contributes to the “everyone has answered” check that releases the
chamber forward — set to false if the bot is a passive
confederate that shouldn't gate progression. showInResults decides
whether the submission appears in any aggregated results display participants see
at the segment's end. tagAsBot decides whether the submission is
visually marked as bot-origin in the UI; the default is false, so the
bot is indistinguishable from the humans, which is usually what a confederacy
design requires.
input deserves a brief separate note. Free-text submissions raise
sharper measurement-validity questions than categorical or ordinal ones: a bot's
prose is harder to compare across sessions than a chosen option, and small
differences in wording can have outsized effects on the humans who read it. The
recommended pattern when using input with LLM-generated mode is a
tightly scoped system prompt, the raw model output logged in the export for audit,
and tagAsBot: true whenever participants will read the submission and
the design should be transparent about its bot origin.
A worked example: a confederacy study runs a selection segment
(“Which option do you find more compelling?”) followed by an
input segment (“In one sentence, why?”). Two configured
bots use static mode in the selection segment, picking option A
in every session; in the input segment they switch to LLM-generated
mode under a system prompt instructed to elaborate on option A in plain, peer-like
language. The participant sees four answers in each segment — two human, two
bot — and across sessions the design holds: the bots' selections are
reproducible to the index, and the bots' free-text answers vary in surface form
while cohering around the same content.
5.5.4 Agent built-in tools (Claude Agent API)
The fourth mechanism is internal to the agent itself, and only applies to participants of the agent type (§ note on non-human participants). A Claude Agent has access to a vocabulary of built-in tools provided by the Anthropic Agent API — tools for reading files in a configured document area, executing small commands, and browsing the web. Before producing the message it will eventually send to the chat, the agent's underlying model can autonomously decide to invoke one or more of these tools, examine the results, and iterate.
A typical pattern, from the chamber's point of view:
- A trigger fires that asks the agent to respond.
- The agent reads (silently) the section of the configured study brief that is relevant to the conversation so far.
- The agent runs (silently) a short check against a dataset to confirm a number it is about to cite.
- The agent produces a single chat message that quotes the relevant passage and reports the number.
Steps 2 and 3 are internal to the agent. Participants in the chat see only step 4 — a single grounded reply. The agent's internal trace (which tools it called, with what arguments, and what results it got back) is preserved in the exported data for the researcher to audit, but it is not shown to other participants.
The two design decisions a researcher makes for an agent are therefore:
- Tool scope. Which of Claude's built-in tools to enable, and — for the file-reading tool — what document area to expose. The narrower the scope, the more focused the agent's contributions; the wider the scope, the more open-ended.
- Step budget / latency. Agents take longer per response than LLM chatbots, because they loop. Configure a maximum step count (or wall-clock budget) so the agent does not hold up the chamber. The platform shows a “thinking” indicator while the agent is in its loop.
Two notes on relating 4.5.4 to 4.5.2:
- An agent acting as mediator can fire LLM-chosen Carrier actions (4.5.2) and use its built-in tools (4.5.4) in the same turn. The former affects the chat; the latter informs the model.
- The two channels are logged separately in the exported data. The Carrier action log records intervention actions; the agent trace records tool invocations.
5.5.5 Choosing among the four mechanisms
| If you want… | Prefer… |
|---|---|
| Identical turn-taking enforcement across sessions | Scripted actions (4.5.1) with fixed release conditions. |
| Facilitator interventions that respond to what was actually said | LLM-chosen Carrier actions (4.5.2), constrained to a small vocabulary. |
| To study the effect of a particular intervention pattern | Scripted — the intervention is the manipulation, so it must be uniform across participants. |
| To study whether an automated facilitator helps at all | LLM-chosen — the manipulation is the model's judgement, so it must vary contextually. |
| A non-human participant that votes, ranks, or writes alongside humans | Segment-submission actions (4.5.3) — pick a data mode according to how reproducible the submission needs to be. |
| A confederacy condition where the non-humans submit identical answers in every session | Segment-submission actions in static mode (4.5.3). |
| A non-human participant that cites the study material accurately | An agent with file-reading enabled over the materials (4.5.4); the agent quotes what it reads. |
| A non-human participant that fact-checks live during conversation | An agent with web-browsing enabled (4.5.4). |
| A non-human participant that runs computations over a dataset before answering | An agent with code execution enabled (4.5.4). |
§5 · Triggers
5.6 Composing triggers
5.6.1 Reading a script: the three views
A chamber's agents are authored together in the Script Builder, which shows the same set of triggers three ways. They are not decoration: each answers a question the others cannot.
| View | Answers |
|---|---|
| Flow | What does each agent have? One column per agent in the chamber, its triggers in order, the fallback it falls back to, and the selected trigger inspected on the right. Drag a type from the rail onto a column to add one; double-click a node for the full editor. |
| Storyboard | What would a participant see? The whole chamber as one read-through, chaptered by segment, with the chained and follower triggers placed after whatever sets them off. Triggers with no fixed time are grouped after the timed ones in each chapter. |
| Timeline | When does it all happen? See below. |
5.6.2 The Timeline view, and what it catches
The Timeline lays the chamber's segments out as bands, one lane per agent, and places a mark for every segment a trigger is armed in. Each band carries its own ruler starting at 0:00, because that is the clock the runtime keeps (§5.3.3). A trigger with no segment scope therefore appears once in every compatible band — which is what actually happens to it.
- A filled pin is a scheduled time, straight from the trigger's delay.
- A hatched bar is a reactive trigger — keyword, regex, after-agent-message. It has no position in time, only a window in which it is armed.
- A bar with an open right edge is an idle timeout: earliest that time, then any time after, or never.
- A dashed violet bar is a chain-only trigger, which has no condition of its own and runs when something else chains to it.
- A grey pin in an amber hatch is armed past the end of its segment. It will not run.
Beneath the bands, a panel reports what the schedule actually does. Every row is computed from the configuration alone — no run required — and each one is a failure that is otherwise invisible until a pilot:
- a time trigger armed past the end of the segment it runs in;
- a trigger with no segment scope that silently re-arms in every segment;
- a chain-only trigger that nothing on any agent chains to;
- a trigger scoped to a segment whose type cannot run it — a response trigger parked on an instruction segment, say.
The same checks run on Deploy, so a script that cannot work is reported before participants meet it rather than after.
5.6.3 Composition modifiers
Real bots rarely consist of a single trigger. The composition surface lets you make the rules interact:
- Priority. Each trigger has a numeric priority. When multiple triggers' conditions are satisfied by the same event, Carrier evaluates them in descending priority order and fires the highest-priority match. Use priority to handle exceptions: an attention-check trigger with high priority can override a greeting trigger that would otherwise also fire.
- Cooldown. A trigger can specify a minimum interval that must elapse between successive firings. Used to prevent a bot from spamming when a condition stays true for a while.
- Max fires. A trigger can cap how many times it ever fires per chamber (e.g. an introduction trigger that only fires once).
- Probability. A trigger can fire with a configured probability less than 1.0 when its condition is satisfied, producing stochastic interventions.
- Chain target. A trigger can specify another trigger's ID to fire after it completes. This is how multi-step behaviours are built: trigger A says something, then chains to trigger B which fires a follow-up question after a delay, which chains to trigger C which records the response. The chained trigger's condition can be
chain-only, which means it can only ever fire by being chained — useful for keeping cascading sequences out of the normal trigger queue. A chain target is resolved across every agent in the chamber, not just the one that owns the chaining trigger, so one agent can hand off to another — which is why the Script Builder shows all of a chamber's agents side by side, and draws the chains between them.
Together, these modifiers turn a flat list of triggers into a directed graph of contingent behaviour. Most experiments need only flat lists; the modifiers are there for designs that demand them.
5.6.4 Variable conditions in triggers (again)
The variable-filter modifier from §5.3.2 deserves a second mention here because of its compositional consequences. A single trigger list with variable filters
TRIGGER 1 if var.condition == "treatment", respond with X
TRIGGER 2 if var.condition == "control", respond with Y
is functionally equivalent to two bots, one per condition, with one trigger each. Whether to write the contrast as “one bot with two filtered triggers” or “two bots, one per condition” is a design decision: the former keeps the experiment shorter and easier to read; the latter is sometimes clearer when the two conditions differ in many small ways.
§5 · Triggers
5.7 Builder walkthroughs
Triggers are per-chamber, so the researcher places the agent in a chamber first and opens its Script Builder. They drag the Keyword Match template onto the agent’s flow, which opens the trigger editor: keyword climate, a response message, a 1500 ms delay and a probability of 0.7. Saving drops the trigger into the flow between Segment start and the agent’s Fallback, labelled with its condition type. The fallback panel at the foot of the column shows the reply the agent gives when nothing matches, or says so when none is set.
In the Script Builder the researcher first drags in a Chain Only trigger — one that never fires on its own — and gives it the response “Thanks for sharing.” They then drag in an Event Monitor, set its Activate On mode, and point its On Success → Trigger ID at the chain-only trigger. Both now sit in the agent’s flow, the second reachable only through the first. (Chaining is expressed through the event monitor’s success/failure targets and through LLM-mode deactivation, not as a generic field on every trigger.)
The researcher drags the LLM Mode template onto an agent’s flow in the Script Builder. The trigger editor asks when the agent enters LLM mode (Activate On — here Segment Start), for the system prompt (“facilitate a balanced discussion…”), the provider and model, who it responds to (Target — here humans only), and how it leaves LLM mode again (Deactivation, which can itself chain to another trigger). The saved node shows the whole rule in one line: llm-driven: segment-start → LLM (timeout).
This concludes Part I. The four systems — chamberlines/chambers/segments, roles, variables, and triggers — together define everything a Carrier experiment can express. Part II turns to running an experiment built with them.
Part II · §6. Running and monitoring experiments
Part II is short by design. Most of what makes Carrier worth using is in Part I; what follows is the day-to-day mechanics of running a study built with the four systems above.
§6 · Running
6.1 The experiment lifecycle
Every experiment moves through a small lifecycle. Its status field — visible at the top of the builder and on the dashboard — takes one of five values:
| Status | Meaning |
|---|---|
| Draft | The experiment is being edited. Participants cannot enter it. |
| Active | The experiment is open. New participants who visit the URL begin a run. |
| Paused | New participants are blocked, but existing runs continue. Use during pilots when you want to freeze enrolment without disrupting in-progress sessions. |
| Completed | The experiment is closed. No new runs; existing data remains exportable. |
| Archived | The experiment is hidden from the main dashboard listing. Data remains exportable. |
The transition from Draft to Active is the activation step. The builder will refuse to activate an experiment that has obvious gaps — no chamberlines, an unfilled bot template, an invalid variable reference — but it will not catch every error. Pilot every experiment against yourself (and ideally a colleague) before opening it to real participants.
§6 · Running
6.2 The dashboard at a glance
The dashboard is the experimenter's command surface during a live experiment. It has four panels, of which the first three are tightly coupled.
Active sessions. A list of every participant currently in a run, with their current phase (initialisation / identity setup / global pre-survey / chamber line execution / global post-survey / completed), the chamberline they were assigned to, and the index of the chamber they are currently in. Clicking a participant opens a per-participant detail view.
Matching queue. A list of every participant currently waiting to be matched into a chamber. Each entry shows the chamber the participant is waiting for, how long they have been waiting, and what slot constraints (§4.3) need to be satisfied for them to be admitted. A queue that grows steadily during an experiment is the symptom of a constraint that is too tight.
Alerts. A rolling list of events that warrant attention: disconnects, long waits, drop-outs, and idle participants. The dashboard surfaces these in priority order; experimenters typically watch this panel rather than the others.
Chatrooms. A list of every active chatroom (matched chamber). Each entry can be opened to show the live transcript, the broadcast log, the action log, and the processor-interaction log. This is the place to watch a chamber unfold in real time.
The dashboard auto-refreshes; no manual refresh is required.
§6 · Running
6.3 Live monitoring and intervention
Three kinds of intervention are available from the dashboard during a live session.
Pause a participant's run. Halts the run at its current phase. The participant sees a paused indicator; segments and chamber timers do not advance. Resume with a single click. Use when a participant has hit a problem you want to debug before they continue.
End a participant's run. Terminates the run with a configurable completion message. The participant is shown the message and the global post-survey is skipped (unless explicitly forced). Used for participants who cannot continue — disconnections that will not heal, attention-check failures, withdrawal requests.
Host-advance a segment. Forces a segment whose transition mode is host (see §2.5) to advance for the chamber. Used during pilots to step a chamber through its timeline without waiting for timers or for participant clicks.
A fourth, lighter intervention — broadcast a message into a chatroom — is available from the chatroom detail view. The message appears in the chat as an experimenter announcement. Use sparingly: every dashboard broadcast is recorded in the chat transcript, so it becomes part of the dataset.
§6 · Running
6.4 Exporting data
Data export is the final step of an experiment. It is available from the experiment detail page and the dashboard.
Two parameters control what comes out:
| Parameter | Choices | Effect |
|---|---|---|
| Format | JSON · CSV | The serialisation of the export. JSON preserves nesting; CSV flattens. |
| Type | All · Participants · Chatrooms · Responses | What subset of the experiment to include. |
The four export types correspond to four levels of granularity:
- Participants. One row per participant per chamberline assignment, with their identity, demographics, status, and variable values.
- Chatrooms. One row per chatroom (matched chamber), with the chamber's participants, settings, and timestamps.
- Responses. All survey responses across all surveys (global pre, global post, chamber pre, chamber post, embedded segment surveys), keyed by participant and survey ID.
- All. Every preceding type, plus the full chat transcripts and the broadcast / action / processor-interaction logs.
For a quantitative analysis pipeline, Responses and Participants in CSV are usually the right starting point; for a qualitative pass over conversation, All in JSON gives you the structure to operate on.
Exports are produced on demand; there is no waiting queue. For very large experiments, the export endpoint accepts a participant filter, so you can export a single chamberline or a single date range without downloading the entire experiment.
For a category-by-category description of what each export actually contains, see §6.5.
§6 · Running
6.5 What's in your data
An export is not a single thing. It is a layered snapshot of a run viewed from several angles — the survey angle, the conversation angle, the timing angle, and so on. Most analysis questions touch two or three of these layers at once. This section walks through what Carrier captures for every run, what it does not capture, and which export type each kind of data lands in.
The map below shows, at a glance, which export type carries which category. All is a superset; researchers who plan to do anything beyond the simplest summary should default to it.
| Data category | Participants | Chatrooms | Responses | All |
|---|---|---|---|---|
| Identity, assignment, status | • | • | ||
| Survey responses | • | • | ||
| Chat transcripts | • | • | ||
| Timing and pacing | • | • | • | |
| AI / processor interactions | • | • | ||
| Behavioural events | • | |||
| Attention checks and face monitoring | • | • | ||
| Assignment and reproducibility | • | • |
The eight sub-sections that follow describe each category in turn, including the cases in which a category is empty by design.
6.5.1 Survey responses
For most studies, this is the primary data. Carrier captures survey responses in four places:
- The global pre-survey, completed before any chamber, once per run.
- The global post-survey, completed after the final chamber, once per run.
- Chamber pre- and post-surveys, completed at the boundaries of each chamber.
- Embedded segment surveys — the
surveysegment type — completed within a chamber as part of its segment timeline.
Two shapes come out together. The raw Survey.js JSON preserves nested question
structures (matrices, panels, conditional branching) and is appropriate when the
response shape itself matters. The flattened response rows give one row per
question per participant, with questionId, questionText,
response, responseType, and a stage indicator
pointing at the survey instance the answer belongs to.
For most quantitative pipelines, Responses in CSV is the right starting point. For qualitative analyses or for questions where the survey was deliberately non-trivial, All in JSON preserves the structure you need to operate on.
6.5.2 Chat transcripts
Every message exchanged in every chatroom is preserved verbatim. Each message carries a sender (human participant, LLM chatbot, scripted chatbot, agent, mediator bot, or system), an ISO timestamp, and a message type that distinguishes ordinary text from system notifications, joins and leaves, mediator broadcasts, bot and AI responses, and processor suggestions.
System messages are interleaved with the conversation rather than stored on the side, which means a researcher reading the transcript chronologically sees joins, disconnects, broadcasts, and attention-check events in situ. Chatrooms and All exports include the full chat history; Participants does not.
6.5.3 Timing and pacing
Several layers of timestamps come out together.
- Run-level. When the run started, when each phase transitioned, when the run completed or was terminated, and the reason for termination.
- Chamber-level. When matching happened, when the chatroom began, when each segment within the chamber started, when the chamber ended, and the actual elapsed duration.
- Per-message. Every chat message carries an ISO timestamp.
- Connection-level. Heartbeats, reconnection counts, and the participant's total time in the experiment.
These together let researchers reconstruct any per-participant duration of interest — time-to-first-message, time between segments, time spent re-reading instructions — without custom instrumentation.
6.5.4 AI and processor interactions
When chambers use the processor role, every assist event is logged with its full text. Review interactions carry the draft text that was submitted, the feedback that came back, and whether the communicator accepted, rejected, or edited the suggestion. Generate interactions carry the request and the generated response. Real-time assist suggestions carry their content and outcome.
For chambers that use an LLM chatbot, mediator, or agent, the model's reply is stored in the chat history alongside human messages, with sender metadata identifying the role and, where set, the provider. For agents on the Claude Agent path — where memory is provider-managed — the provider's session handle is preserved on the chatroom so that Carrier-side and provider-side timelines can be aligned after the fact.
6.5.5 Behavioural events
When client-side instrumentation is active for a segment, Carrier captures a stream of low-level events: tab visibility changes, focus changes, pointer activity, clicks, and a small set of custom events raised by specific segment types. Per-segment summaries are produced automatically — most commonly tab-away count and total tab-away time — and the raw event stream is preserved for replay or fine-grained sequence analysis.
This data is opt-in by segment. Researchers who want it should confirm that the relevant segments have behavioural-events instrumentation enabled in the builder before piloting.
6.5.6 Attention checks and face monitoring
The attention-check segment captures a result record per attempt: the
mode (face-based or survey-based), whether it passed, the retry count, and any
mode-specific details. The record appears in two places — a structured array
attached to the run, and a corresponding system message interleaved into the chat
transcript at the moment of the check.
Face monitoring, when enabled on a chat segment, emits its own event stream: warning shown, face returned, grace expired, paused, resumed, terminated. It is stored the same way: a structured array on the run plus interleaved system messages in the transcript.
Both categories are present only when the experiment was configured to produce them. Their absence in an export is not a missing value; it means the experiment did not ask for them.
6.5.7 Assignment and reproducibility
For anyone who needs to reconstruct, after the fact, why a given participant saw what they saw, the export carries:
- The chamberline each participant was assigned to, and the reason (random, counterbalance, survey-based, or fixed).
- A frozen snapshot of the participant's run plan — the chambers in their assigned order, each with its role and slot assignment for that participant.
- The experiment's version at the moment the run was created, so that a later configuration change does not corrupt the interpretation of earlier runs.
- A condition seed where randomisation was involved.
Combined with the admin-side activity log (see §7), this is sufficient to reproduce a participant's path through the experiment exactly.
6.5.8 What's conditional
Several categories appear only when the experiment is configured to produce them. Worth flagging up front, so that an absent column is not mistaken for a bug:
- Behavioural events require client-side instrumentation enabled on the relevant segments.
- Attention-check results require an
attention-checksegment in the chamberline. - Face-monitoring events require face monitoring enabled on a chat segment.
- Processor interaction logs require at least one chamber to use a processor role.
- Non-human sender metadata (role, provider) is populated when the message originates from a chatbot, mediator, or agent; for human messages those fields are empty by design.
- Variable values appear only for variables the experiment defined; there are no system-provided demographic variables.
If a researcher expects one of these and finds it missing, the place to check is the experiment configuration, not the export.
§6 · Running
6.6 Pilot first, ramp second
A short note that does not fit anywhere else in this guide but matters in practice. Every Carrier experiment benefits enormously from a small pilot — three to five participants, ideally including the researcher themselves — before being opened to a larger sample. Pilots are the only reliable way to catch the kinds of issues that the builder cannot validate: a slot constraint that is unsatisfiable in practice, a chamber timing that is too short to read the instructions, an LLM mediator whose system prompt produces unexpected behaviour on real conversations, a survey question that is ambiguous to actual participants.
Pilot with the experiment status set to active and the dashboard open. Watch the matching queue, watch the chat transcripts, and watch the action log. Most experiments end up requiring at least one round of revision after the first pilot. This is normal; budget time for it.
Part II · §7. Administration
Accounts, collaborators, and the admin portal — the parts of Carrier that exist to keep multiple researchers working on the same platform.
§7 · Administration
7.1 Accounts and collaboration
Every researcher account in Carrier has a role: either researcher or admin. Researchers can create, edit, run, and export their own experiments; admins additionally manage the user list and the activity log.
An experiment has one owner and any number of collaborators:
- The owner can edit everything, transfer ownership, add and remove collaborators, and delete the experiment.
- A collaborator can edit the experiment's configuration and view its data, but cannot transfer ownership, add other collaborators, or delete the experiment.
This separation is the simplest model that supports the common pattern of one PI owning each study and several lab members helping to configure and run it.
§7 · Administration
7.2 The admin portal
The admin portal is available only to users with the admin role. It exposes three sub-areas.
User management. Create, update, enable, and disable user accounts. Disabling an account preserves all of the user's experiments and data but prevents them from logging in. This is the right action when a lab member leaves; deletion is rarely necessary.
Registration approval. When self-registration is enabled, new sign-ups arrive in a pending state. The admin reviews each request — typically by checking the requester's institutional email and the project they intend to use Carrier for — and approves or rejects.
Activity logs. A chronological log of meaningful actions across the platform — logins, experiment creations, role changes, exports. Useful both for accountability and for understanding usage patterns when scaling the platform across multiple labs.
Appendices
Glossary, type × role matrix, and quick-reference indexes for segment types and trigger types.
Appendices
Appendix A · Glossary
| Term | Definition |
|---|---|
| Aggregate variable | A variable computed over multiple participants in a chamber. Configurable to include or exclude bot/agent participants. |
| Active-segment filter | A list of segment IDs during which a trigger is eligible to fire. Empty list = fire in any segment. |
| Chain target | Another trigger ID that fires after this one completes. Used to compose multi-step bot behaviour. |
| Chamber | A timed grouping of matched participants who share the same segments and remain together until the chamber ends. |
| Chamberline | An ordered sequence of chambers, representing one experimental condition. A participant is assigned to exactly one. |
| Chamberline filter | A condition under which a participant is eligible for a given chamberline; used by survey-based assignment. |
| Chatroom | The live, runtime instantiation of a chamber for a particular matched group. |
| Communicator | The role of a primary conversational participant. The “default” role in any chamber. |
| Embedded segment | A selection or ranking segment displayed as an overlay on a chat segment, so participants can vote or rank without leaving the conversation. |
| Global pre-survey / post-survey | Surveys at the very start and very end of a run. Distinct from chamber-level surveys. |
| Agent | An autonomous non-human participant built on Anthropic's Claude Agent API. Has built-in tools for reading files (in a configured document area), running code, and browsing the web; uses them on its own initiative to inform its messages. Distinct from an LLM chatbot. |
| Agent built-in tools | The file-reading, code-execution, and web-browsing tools available to an agent via the Claude Agent API. Used for information gathering; distinct from Carrier intervention actions. |
| LLM chatbot | A non-human participant that produces chat messages from a language model, with no tools and no scripted rules. Open-ended, varies across sessions. |
| LLM-chosen Carrier action | An intervention action (disable_chat, prompt_participant, …) selected at runtime by an LLM-driven participant via its structured response. Available to any LLM chatbot or agent acting as mediator. |
| LLM-driven response | A response produced by a language model on the fly, rather than from a pre-written script. |
| Match | The event of assembling enough participants of the right kinds to fill a chamber's slots. |
| Mediator | The role of a facilitator participant — sees everything, broadcasts, controls turn-taking. |
| Non-human participant | Umbrella term for the three kinds of non-human entity Carrier supports: LLM chatbots, scripted chatbots, and agents. |
| Phase script | An ordered list of phases for a processor, each with a mode and a transition trigger. |
| Priority | A numeric ranking among triggers; higher priority fires first when multiple triggers match. |
| Processor | The role that assists composition before a communicator's text becomes a message. Three modes: review, generate, real-time assist. |
| Response | The message a trigger sends when it fires. Either scripted or LLM-driven. |
| Run | One participant's complete pass through the experiment, from arrival to completion. |
| Scripted chatbot | A rule-driven, deterministic non-human participant. Configured by triggers; produces pre-written messages. Can fill communicator and mediator roles, but not processor. |
| Scripted response | A pre-written message (or random pick from a list) sent when a trigger fires. |
| Segment | An activity within a chamber: a chat, a slide, a survey, a timer, a vote, etc. |
| Slot | A position in a chamber, with a type (human / LLM chatbot / scripted chatbot / agent) and a role (communicator / mediator / processor). |
| Standalone segment | A segment that occupies the participant's entire screen, as opposed to embedded. |
| Trigger | A condition–response–action rule that governs when a non-human participant speaks or acts. |
| Variable | An attribute attached to a participant, used for matching, visibility, interpolation, or trigger conditions. |
| Visibility condition | A condition on a chamber that, if false, causes the participant to skip the chamber. |
Appendices
Appendix B · Type × Role compatibility matrix
| Type \ Role | Communicator | Mediator | Processor |
|---|---|---|---|
| Human | ✓ | ✓ | ✓ |
| LLM chatbot | ✓ | ✓ | ✓ |
| Scripted chatbot | ✓ | ✓ | — |
| Agent (Claude Agent API) | ✓ | ✓ | ✓ |
Reproduced from §3.1 for quick reference. The only forbidden combination is scripted chatbot as processor.
Appendices
Appendix C · Segment types — quick index
Every segment type, with the section that documents it. The descriptions live in §2.2; this index is deliberately a pointer rather than a second copy of them.
| Type | Reference | Compatible with AI | Embeddable |
|---|---|---|---|
slide | §2.6 | — | ✓ |
instruction | §2.7 | — | ✓ |
media | §2.8 | — | ✓ |
pdf | §2.9 | — | ✓ |
timer | §2.10 | — | ✓ |
survey | §2.11 | — | ✓ |
input | §2.12 | ✓ | ✓ |
selection | §2.13 | ✓ | ✓ |
ranking | §2.14 | ✓ | ✓ |
chat | §2.15 | ✓ | — |
social | §2.16 | ✓ | ✓ |
task | §2.17 | ✓ | ✓ |
process | §2.18 | ✓ | — |
attention-check | §2.19 | — | — |
Appendices
Appendix D · Trigger types — quick index
| Type | Listens for | Notes |
|---|---|---|
keyword | Configurable word / phrase | Most common. |
regex | Regular expression match | Use for structured patterns. |
time | Delay from chamber / segment start | Fires regardless of chat activity. |
message-count | Total messages in chatroom | Fires once per matching count. |
participant-message-count | Messages from a specific participant | Supports total / consecutive / since-reset. |
sequence | Ordered series of matches | For staged steering. |
participant-action | Join, leave, idle, etc. | Fires across segments. |
after-bot-message | Another bot's message | Cross-bot chaining. |
event-monitor | Arbitrary chatroom event | Catches segment transitions, dashboard interventions. |
chain-only | (Passive) Only fires from chain | For multi-step bot behaviour. |
llm-driven | An LLM judges the condition | Most expressive; least replicable. |
periodic | Fixed interval | Mediator-specific. |
aggregate | N messages in a window | Mediator-specific. |
topic-detected | Topic / keyword pattern | Mediator-specific. |
activity-timeout | Inactivity duration | Mediator-specific. |
participant-count | Active participant threshold | Mediator-specific. |
discussion-phase | Chamber start / middle / end | Mediator-specific. |
Appendix
Appendix E · Walkthrough — building a social feed
A tour of the Feed Builder, from the seeded demo experiment to a feed saved in the database. It covers the same ground as §2.16 in the order you would actually meet it.
Every step below is executed by e2e/social-feed-builder.spec.js
against a real server and a real database, and the screenshots are the ones
that run produces. If the walkthrough and the product ever disagree, the test
fails — which is the point of writing it this way. To run it yourself:
npm run demo:social seeds the demo and starts the app;
npm run test:e2e walks this page in a browser and checks it.
1 · General — the workspace
Three columns, and they are the whole job. The outline on the left is the feed as a timeline of arrivals, with the cast of accounts beneath it — two panes scrolling independently, so a feed of a hundred posts never buries the cast. Agents get a row each with their binding inline; crowd accounts, which a CSV import derives one-per-handle and which do nothing at run time, wrap as chips. The editor in the middle follows whatever is selected. The preview on the right is the participant’s own view, rendered in the participant stylesheet rather than an impression of it.
2 · Layout is not presentation-only
Switch the layout above the preview and three things move together: the preview re-renders, the affordance chips change (votes lights up only on Reddit), and the Forum fields group opens. On the other two layouts that group is dimmed and states its own condition, because those layouts render none of it and cannot collect a vote at all. This is the setting most worth understanding before you run anyone — see the validity note in §2.16.
3 · Edit a post, and see it as a participant would
Pick a post in the timeline and the centre column follows; the preview updates when you leave the field. Timing is authored as a direction and a unit — before entry, at entry, after entry — rather than as raw milliseconds. Seed comments are edited as the thread they already are, with Reply inserting a child; on Instagram and Facebook that button disappears on a comment that is itself a reply, because those layouts render one level only.
4 · Save refuses an invalid feed, and says why
A new seed comment starts empty, and an empty comment is not a valid feed. The same goes for a post with no caption, and for an agent account bound to nothing. Worth knowing before you fight it: an unbound agent account is an error, not a warning — the feed will not save and the experiment will not deploy until you bind it or turn it back into a crowd account.
5 · Find things in a long feed
The filter matches post id, caption and author, and never renumbers anything, so it is safe to filter and then act on what is shown. Past eight distinct arrival times the timeline groups into Before entry · On entry · After entry and moves each post’s own offset onto its row.
6 · Agents — who replies, and how
An agent account’s persona prompt, provider and model, temperature, context window, when it speaks and its reply delay — all without leaving the Feed Builder. These fields belong to the chamber agent, not to this feed: it is the same agent card that sits on the builder canvas, so the edits apply immediately rather than waiting for the feed’s Save.
7 · An account with nothing behind it
Select an unbound account and there is no form to fill — there is nothing to configure until something replies as it. The tab offers the bind instead, and the readiness chip in the header counts it among the things to fix. An agent already bound to another account is offered disabled, with the reason: one chamber agent answers as one account.
8 · Closing the readiness loop
The other way out: in General, select the account and set its kind to Crowd. It keeps authoring posts and seed comments and stops claiming it will answer; the agent-only fields (binding, scope, reply delay) go with it, and the readiness count falls.
9 · Feed — every post at once
A sortable table of the whole feed, with row selection and bulk Retime, Reassign author and Delete. Above it, arrival on a real axis in three bands, because a backlog post at −2h and a post at +45s cannot share one linear scale without collapsing the segment itself to a sliver. The amber band is posts scheduled after the segment ends — authored, exported, and never seen.
10 · Deploy
Save closes the Feed Builder and writes to the builder’s model; the experiment reaches the database through the builder’s own Update, which re-validates every segment — blocking on errors, pausing on warnings. The warning here is the honest one: reddit-only fields authored while the layout is Instagram. They stay in the data and come back if you switch, but they are not rendering now.
The test asserts against the server, not the screen:
- the caption edited in step 3 is in the saved configuration;
- the two posts retimed in step 9 are stored at
showAt: 90000; - the post added in step 5 is present, and the layout is back to
instagram; - the account switched to crowd has lost
boundAgentandscopePostIds; - the persona typed in step 6 reached the chamber agent’s llm-driven trigger, and not a copy inside the feed configuration.
Annotator Documentation
The Annotator is a batch LLM annotation engine for processing text data at scale. Upload a CSV, configure LLM annotators, and download structured results.
Getting Started
What is the Annotator?
The Annotator is a batch LLM annotation engine. Upload a CSV, configure one or more LLM annotators with prompt templates, run the task at scale, and download structured results.
Common use cases include text classification, sentiment analysis, content coding, and replicating published annotation schemes from peer-reviewed research.
Getting Started
Key Concepts
| Concept | Description |
|---|---|
| Task | Top-level container holding CSV data, LLM configs, and processing settings |
| Row | One CSV record, processed independently |
| LLM Config | A provider + model + prompt template combination |
| Repetition | Running each row through each config multiple times for reliability |
| Template | Reusable annotation configuration that can be shared |
| Work Unit | One row × one config × one repetition = one API call |
Getting Started
Your First Annotation Task
Get started in four steps:
Your CSV should contain the text you want annotated. Column names become template variables.
Choose a provider and model, then write a prompt template using
{{columnName}} syntax to reference your data.
Start processing. The engine sends each row through your LLM config and stores the results.
Export your annotated data as CSV, Excel, or JSON.
Getting Started
Providing API Keys
The Annotator requires API keys for the LLM providers you use: OpenAI, Anthropic, and/or Google.
User-level keys are set in your account settings and reused across all your tasks. Per-task keys can be provided when creating or editing a task and override user-level keys for that task only.
Task Setup
Upload & Preview CSV Data
Upload a CSV file (max 10 MB). After upload you can preview the headers and the first
rows of data. Column names become {{columnName}} template variables for use
in your prompt templates.
Task Setup
Configure LLM Annotators
Add one or more LLM configurations to a task. Each configuration specifies a provider (OpenAI, Anthropic, or Google), a model, and prompt templates. You can add multiple configs to compare models or prompt strategies side by side.
Each config supports temperature and maxTokens settings
to control response variability and length.
Task Setup
Write Prompt Templates
Each LLM config has a system prompt and a user prompt.
Use {{columnName}} syntax to insert values from each CSV row into the prompt.
Task Setup
Set Repetitions
Set between 1 and 20 repetitions per row per config. Multiple repetitions let you measure reliability and use majority voting to determine final labels.
The total number of work units (API calls) is:
rows × configs × repetitions.
Processing
Estimate Costs
Before running a full task, use the cost estimator. It runs a sample of up to 10 rows, measures the tokens consumed, and extrapolates to give you an estimated cost for the complete task.
Processing
Standard Processing
Standard mode streams results in real time using 1–20 parallel workers. Failed requests are retried automatically with exponential backoff. Processing is crash-safe — results are saved per row, so progress is never lost.
Processing
Batch Processing
Batch mode uses the OpenAI and Anthropic batch APIs for approximately 50% cost savings with a 24-hour turnaround. Google requests fall back to standard processing automatically.
Processing
Pause, Resume & Cancel
In standard mode, you can pause processing at any time. All completed results are preserved. Resume picks up where you left off. Cancel stops the task permanently but keeps all results that were completed before cancellation.
Templates
Use Research Templates
The Annotator includes 25+ peer-reviewed annotation presets from published research. Select a template to pre-fill your LLM configs with validated prompt designs.
| Authors | Configs | Domain |
|---|---|---|
| Gilardi et al. (2023) | 7 annotators | Text classification |
| Rathje et al. (2024) | 6 annotators | Psychological text analysis |
| Bhatia et al. (2025) | 3 annotators | Choice dilemma annotation |
| Bojic et al. (2025) | 5 annotators | Latent content analysis |
| Kumar et al. (2026) | 4 annotators | Empathic communication evaluation |
Templates
Create Custom Templates
Save any task configuration as a reusable template. Custom templates are private by default and available only to you. They capture the full LLM config including prompts, model settings, and repetition count.
Results
Monitor Progress
A progress bar shows real-time completion status. Each task follows a status lifecycle:
pending → processing →
completed or cancelled. In standard mode, a
paused state is also available.
Results
Download Results
Export results in CSV, Excel, or JSON format. You can download partial results while the task is still running — useful for spot-checking quality before the full run completes.
Results
Understanding Output Format
Results use a flattened format with one row per input record. Columns include all original input data, the rendered prompts, and response columns for each config and repetition combination.
read.csv().
In Python, use pandas.read_csv(). In
Excel, open the Excel export for automatic column formatting.
Response columns follow the naming pattern
[configName]_rep[N].
Carrier Workspace
Carrier Workspace brings your research team's Claude Code activity — session transcripts and shared memory — into one place inside Carrier, so the way your team used AI assistance to build and analyse a study is searchable, reviewable, and preserved alongside the study itself.
Overview
What is Carrier Workspace?
When a team uses Claude Code while building an experiment, writing analysis scripts, or
preparing materials, each developer accumulates a local history of sessions
(the back-and-forth transcripts of their work) and memory (durable notes
the assistant keeps about the project). That history normally lives buried in each person's
local ~/.claude directory, invisible to the rest of the team.
A workspace collects that data for a single repository and shows it on one
page in Carrier. Carrier Workspace is powered by the team-claude-view skill,
which provides the small client scripts that package and upload a machine's data, plus a
/private command for marking sessions you don't want shared.
A workspace is tied to one repository, and you choose how its data arrives when you create it. There are two modes:
| Mode | How data arrives |
|---|---|
| Carrier Workspace mode (default) | Each developer's machine packages its local Claude Code cache and uploads it to Carrier with a small Python client. Works with no GitHub repository involved. |
| GitHub-linked mode | Carrier connects to a GitHub repository and pulls the shared data automatically on a schedule. Lowest-effort once set up — nobody runs anything by hand. |
Both modes end up in the same place: a workspace page showing sessions and memory.
Overview
When a research team needs it
This is a team-tooling feature, separate from running experiments. It does not touch participant data or your experiment configuration — it concerns how your team worked, not what your participants did.
The deeper reason to keep this record is delegation. Empirical research now routinely hands real methodological work to agentic AI: cleaning a dataset, deciding which records to exclude, choosing a transformation, drafting an analysis script, selecting a model specification. Those are not neutral chores — they are methods decisions, and when an agent makes them they tend to vanish the moment the session closes. A workspace turns that delegated work into a durable, shareable record of what was asked, what the assistant decided, and why. Making AI use visible in this way is squarely in the spirit of open science: the same disclosure norms that ask us to share data, code, and pre-registrations extend naturally to disclosing how AI shaped the work.
Transparency also guards against a subtle integrity risk that agentic workflows can introduce without anyone intending it. An assistant pointed at a loosely specified goal — “find the effect,” “get the model to fit,” “clean this up so the result holds” — can quietly explore many exclusion rules, covariate sets, and specifications, then surface only the one that reaches significance. That is the garden-of-forking-paths / researcher-degrees-of-freedom problem, arrived at as unintentional p-hacking rather than deliberate fishing. Because the workspace preserves the full transcript — every fork the agent tried, not just the final answer — you, your collaborators, reviewers, and your future self can tell whether a reported result survived a single principled analysis or emerged after dozens of silent attempts. The record makes the exploration auditable, which is precisely what keeps delegation honest.
Concretely, reach for it when:
- Reproducibility & provenance. You want a durable record of how AI assistance produced study materials, analysis code, or stimuli — and which analytic decisions were delegated — the kind of provenance a methods section or a replication package benefits from.
- Research integrity. You want the agent's exploration to be auditable, so a reported effect can be traced back to a principled analysis rather than an opaque search.
- Onboarding. A new RA or collaborator can read how the project was built rather than starting cold.
- Coordination. Several people on the team use Claude Code on the same repository and you want a shared, searchable view instead of scattered local histories.
Setup
Carrier Workspace mode (default)
This is the default mode. There is no GitHub connection: each developer runs a small Python client (provided by the team-claude-view skill) that bundles their local Claude Code cache and uploads it to Carrier with an API key. The data travels straight from your team's machines to Carrier.
When you create a Carrier Workspace, Carrier displays an API key exactly once, right after creation. Copy and save it now — it is never recoverable. The creation screen also shows the exact upload URL and a ready-to-paste configure command. If you lose the key, you'll need to recreate the workspace to get a new one.
On every machine that should contribute data, run the configure command once. It saves
the upload URL and key locally so later syncs don't need them. Use the exact
--url and --key shown in the create modal:
python3 scripts/team-claude-client/carrier_configure.py --url <upload-url> --key <api-key>
After configuring, push the machine's Claude Code data. The client packages your local
sessions/ and memory/ directories into a gzip tarball and
uploads it, then prints how many sessions and memory entries were sent:
python3 scripts/team-claude-client/carrier_sync.py
Running the sync by hand is easy to forget. Claude Code's SessionEnd hook in
.claude/settings.json fires when a session ends — wire the sync script
in so every finished session uploads automatically:
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python3 scripts/team-claude-client/carrier_sync.py"
}
]
}
]
}
}
Adjust the path if your repo lays the script out differently.
Setup
GitHub-linked mode
In GitHub-linked mode, Carrier holds a personal access token (PAT) for your repository and uses it to keep a private mirror of the shared Claude Code data up to date — nobody has to run anything by hand. Choose this if your team already publishes shared data to GitHub.
Carrier needs read access to one repository's contents. Create a token at github.com/settings/personal-access-tokens/new:
- Repository access — scope to the single repository you're linking; don't grant access to all repositories.
- Repository permissions — set Contents: Read-only. That is the only permission Carrier requires.
- Expiration — a 90-day expiry balances safety against re-linking too often.
Copy the token when GitHub shows it — you won't be able to see it again. A classic
PAT (with the repo scope) also works, but Carrier will show an advisory
banner recommending you switch to a fine-grained, single-repo, read-only token.
From the Workspaces page click Link a repo, choose the
GitHub tab, fill in a name, the repository URL (e.g.
https://github.com/your-org/your-repo), and paste the PAT, then click
Create.
Carrier clones the repository and fetches its claude-team-share branch
— the branch your team uses to publish shared data — and reads its
sessions/ and memory/ directories into a private server-side
mirror. If that branch doesn't exist yet, the workspace simply shows an empty state until
it appears; nothing is broken.
Setup
Choosing a mode
| Carrier Workspace mode (default) | GitHub-linked mode | |
|---|---|---|
| Auth | API key, shown once, bcrypt-hashed | Fine-grained PAT (Contents: Read), encrypted at rest |
| Cold start | Create workspace, save key, run carrier_configure.py per machine |
Create token, paste into the GitHub tab |
| Updates | Run carrier_sync.py (or a SessionEnd hook) after each session |
Automatic — Carrier polls ~every 30s; Re-sync now for immediate |
| Works without GitHub | Yes — no GitHub repo needed | No — requires a repo and the claude-team-share branch |
| Data path | Straight from your machines to Carrier | From GitHub to Carrier |
Pick Carrier Workspace mode (the default) when you want to keep data flowing
through your own machines or have no GitHub repo in the loop. Pick
GitHub-linked mode when your team already publishes a
claude-team-share branch and you'd rather not run a client by hand.
Using & maintaining
Browsing sessions & memory
However the data arrives, the workspace page presents it the same way. Open a workspace from the Workspaces page to see two things:
- Sessions — the Claude Code transcripts contributed to this repository. Open one to read it as a linear transcript of the work.
- Memory — the durable notes the assistant kept about the project.
In Carrier Workspace mode, every machine that runs the client contributes to the same workspace: Carrier derives the workspace from the repository's root folder name, so all checkouts of the same repository map to one workspace and each machine's contribution is additive.
Using & maintaining
Keeping data current
How a workspace stays fresh depends on its mode:
- Carrier Workspace mode — data updates whenever a machine runs
carrier_sync.py. The recommended setup is theSessionEndhook (see setup, step 4), so every finished session uploads on its own. - GitHub-linked mode — Carrier polls the repository roughly every 30 seconds and re-syncs stale workspaces automatically as your team pushes new data. Use the Re-sync now button on the workspace page for an immediate refresh.
Reference
Privacy & security
- GitHub PATs are encrypted at rest using AES-256-GCM. Carrier stores the encrypted token, not the plaintext.
- API keys are bcrypt-hashed. They are shown once at creation and never recoverable — Carrier cannot display or email them again.
- Marking a session private. The team-claude-view skill provides a
/privateslash command that marks a session as non-shareable, so it won't be included when your data is shared. - Removing a workspace deletes its data. Carrier deletes the server-side mirror and, for GitHub-mode workspaces, the encrypted token along with it.
Reference
Troubleshooting & FAQ
Carrier Workspace mode (manual upload)
| Symptom | What it means |
|---|---|
| 401 Unauthorized | The API key is wrong or has been rotated. Re-run carrier_configure.py with the correct key for this workspace. |
| 413 Payload Too Large | The tarball exceeded the 50 MB upload cap. Trim older sessions from your local cache before syncing again. |
| 400 Bad Request | The upload was rejected as malformed — usually a corrupt tarball, or one containing paths outside the allowed sessions/ and memory/ prefixes. |
GitHub-linked mode
| Symptom | What it means |
|---|---|
| Auth-error banner | The PAT expired or was revoked. Create a fresh token and re-link the repo (or update the token on the existing workspace). |
| Empty state persists | The claude-team-share branch doesn't exist on the remote yet. The workspace stays empty until the branch is created and pushed. |
| Can't read a private repo | Make sure the PAT actually has access to that specific repository. |
Common questions
How do multiple machines work together? In Carrier Workspace mode, every machine that runs the client contributes to the same workspace — Carrier derives the workspace from the repository root's folder name, so all checkouts of the same repository map to one workspace. Each contribution is additive.
Can I share privately, without GitHub? Yes — that's exactly what Carrier Workspace mode is for. The data travels straight from your machines to Carrier with no GitHub repository in the loop.
How do I rotate a GitHub PAT? Create a new fine-grained token and either re-link the repo or update the token on the existing workspace, then revoke the old token on GitHub.
How do I regenerate an API key? There is no in-place regenerate. Delete the
workspace and recreate it to issue a fresh key (shown once), then reconfigure each machine
with carrier_configure.py using the new key.
What gets deleted when I remove a workspace? Its server-side mirror of sessions and memory. For GitHub-mode workspaces, the encrypted PAT is deleted as well. After removal there is nothing left server-side for that workspace.