Bristlenose · mockup

Person actions, everywhere

The atom is "p4 is a human called Jane Smith". Everything else in this document is a refinement of that sentence, and the refinements are ranked — not delivered together.

A person reference is rendered on six surfaces today (a segment badge, a session's speaker entry, a quote attribution, the moderator line, the sessions sidebar, and the proposed People lens). The same object, the same vocabulary, at five altitudes. Every altitude reads and writes — the lens is not a read-only roll-up and the badge is not a write-only control. You act where you noticed the problem, which is already the stated principle in design-speaker-editing.md: fix in context, not up front.

Make the common case trivial and the edge cases possible. It is not Bristlenose’s job to work identity out — it is its job to go with the typical case and make a wrinkle easy to see and cheap to fix. And typical is small: fewer than 20 participants and usually no more than 12, three or four colleagues, one moderator in about 95% of studies, and where there is an extra person in the room they are almost always an observer rather than a second moderator. Every list here fits on screen; nothing scrolls, searches or paginates.

Two threads carry the whole document, both in Ward handover app — round 2. Jane Smith, a client-side product manager who sat in to watch: the pipeline recorded her as participant p4 in session 4 when she was actually observing — the common wrinkle. And Mike Alvarez, a colleague who covered session 9 — where he was given m1, the same code as Martin, who ran every other session — the rare one. (The full cast is pinned in docs/design-people.md §H; a handoff sweeps the benches against it, because several early frames still disagree on who holds which code.)

Palette Appearance follows your system light/dark
Artefact Inside a frame is product only — real tokens, real badge and editable-name rules, menus drawn to macOS metrics. No labels that would not ship.
Commentary Hatched, ruled, mono-labelled. Never the product. The two coloured rules used in the legend below appear only in commentary — the shipped menu has no colour coding.
Part 1 — two families, and why they must never share a section Fixing who said these words is a different act from fixing who this speaker is.
  Attribution Identity
The sentence "That was Sarah, not Jane." "p4 is a human called Jane Smith."
What is wrong The words are credited to the wrong speaker. The cast is right. The words are credited correctly. What we think the speaker is is wrong.
Object Transcript segments → speaker_code Speaker → person, name, role
The fix moves Words. Speech leaves one speaker and joins another. Nothing. It relabels. Every word stays where it is.
Where noticed Reading a transcript, in the flow of the conversation. Scanning a session, or a list of people.
Blast radius This turn · these N turns · every turn of this code This session · this study · everywhere
Downstream Quotes may now name the wrong person; per-speaker stats go stale. A role change alters who is eligible for quotes — but only on the next analysis.
Frequency High on raw audio. Near zero on platform transcripts, which arrive already diarised. Every study, every path. The model guesses names on all of them.
Commentary — the lexical rule that keeps them apart

These two families want to sit next to each other in one menu, and they must not. "This Turn Is › Jane" and "This Speaker Is › Jane" read almost identically and do wildly different things: the first silently moves one paragraph of speech, the second relabels a person. A researcher who means the second and hits the first has corrupted a transcript and will not find out for weeks.

So the rule is lexical, not visual: an attribution item always contains the word Turn — "This Turn Is…", "These 4 Turns Are…", "All of p4's Turns Are…". An identity item never does. If the item says Turn, it moves words. If it doesn't, it doesn't. That is one sentence a researcher can hold, it survives translation better than a colour or an icon would, and it does not spend any of the menu's scarce vertical space.

They are also separated by a rule in the menu, and — the part that matters most — they are reached from different anchors. Attribution lives on a segment badge inside the transcript, where you are reading words. Identity lives on a speaker entry or a person row, where you are reading a cast list. The overlap is deliberately small.

Part 2 — the sequence Ranked by how often a researcher needs it, not by how interesting it is to build.
StepThe sentenceHow oftenWhat it costs
1 Say who, and what — from a bank, not a blank field "m1 is me."
"That m1 is Steve" — picked, not typed.
"p4 is a human called Jane Smith."
"Jane is an observer, not a participant."
Every session, every path. Ten to thirty times a study. Observer-instead-of-participant is the single most common role error, and it is structural — see benches 4 and 11. Small — plus one real prerequisite: every act here is drawn with an Undo, and the undo bridge is currently dead. The undo contract is a step-1 gate, not a parallel workstream.
2 Say the guess is right, fix the spelling, separate the two names "Yes, that really is Danny."
"Michel Hurlly is Mickael Hurley."
"He’s formally Michael J., but call him Mike."
Every study with raw audio. Whisper mangles proper nouns; Teams supplies formal names nobody uses aloud. Medium. A recorded name origin per field, a cleared state the next run will not refill, a narrowed write payload, and an editor for the full name — which today has none.
3 This quote is actually that person "They talked over each other — that one was Sarah, not Jane." A few times a study — and a quote is what gets published. Small-to-medium. The speech moves: a whole-turn quote is one segment update; a fragment is a split whose point the quote’s own timecodes supply. Plus the lens needs the membership filter it currently lacks. The escape hatch that makes step 5 postponable.
4 What naming implies "p6 is the same Mary as p3."
"o2 in s3 is the same Jane as o1 in s1."
"The moderator in s9 is Mike, not Martin" → Mike becomes m2.
Follows from step 1 — no separate UX, no blocking decision. Medium. Speaker→person remap, moderator renumber, stats recompute — all bookkeeping behind an act the researcher has already performed.
5 Fix who said this turn "That whole paragraph was Sarah."
"Two people are in that one paragraph — split it."
Concentrated on raw audio. Near zero where the transcript arrives already diarised — now the recommended path. Largest. Batch segment endpoints, split/merge, word-timing division, stats recompute, the quote cascade.
The same person, in another study "This is the same ward sister from round 1." Mostly prevented by step 1 rather than solved later — see bench 12. What remains is reconciling people named before the bank existed. Shrinks to a back-fill, with no matching algorithm in it at all.
Commentary — three things this ordering asserts

Step 1 is most of the value and almost none of the cost. Naming, self-identification and role are the everyday acts: no typing beyond the name itself, no consequence beyond a relabel, no data moved. Shipping only step 1 — on every surface, with the same menu — would close the gap researchers actually feel, which is that the report talks about p4 and they think about Jane.

Attribution is deliberately late, and that is the ordering claim most likely to be wrong. It rests on where the transcripts come from: platform exports arrive already diarised, so turn-level errors are a raw-audio problem, and cloud import has just made the diarised path easier. If the cohort turns out to record locally and transcribe with Whisper, step 5 moves up sharply. That is measurable, not arguable — the source of each session is already recorded on every segment.

"That's me" is the highest-value identity claim in the system and needs no algorithm. The researcher appears in every study they run, so they are the single most recurrent person in the whole corpus — and the one identity the app never has to guess, because the person asserting it is the person. Said once, it seeds cross-study moderator identity for free and removes the most common case from the cross-study back-fill before it is built.

Part 3 — the menu, growing The same menu at step 1, step 2, step 3. Nothing is re-drawn; items arrive.

1 · Step 1, the atom — an unnamed participant

Right-click on p4's badge or name, anywhere it is rendered. Session 4 of round 2. The pipeline heard nobody introduce themselves, so there is no name and no guess.

contextual menu · step 1 proposed
anchor p4 Participant
Commentary
three items, and two of them are one keystroke away from done

This is the whole of step 1 for a participant. No submenus except Role, no scope choices, no header. The anchor is the scope: you clicked p4 in session 4, so the name you type is p4's — and because participant codes are numbered across the whole study, that name is p4's everywhere. Nothing needs to say so.

Deliberately absent: everything from steps 2–6. No Confirm (there is no guess to confirm), no Same Person As, no Turn items — a badge in a sessions row is not standing over any particular turn. A menu that shows six disabled items to advertise a roadmap is a menu nobody reads.

2 · Still step 1 — the moderator, and the one identity that needs no guessing

Right-click m1 in sessions 1–8. The role is already correct. The name is not known, and the Mac knows it.

contextual menu · step 1 proposed
anchor m1 Moderator
Commentary
one click, no typing, and it is reusable forever
Where the name comes from
NSFullUserName() — the Mac's own account name. The app offers it and never assumes it, which is the standing rule for profile connection: the researcher may say "I'm m1", the system may not decide it for them.
Scope
This study. m1 in every session where the app currently believes m1 is one speaker. The sweep line confirms the count afterwards — You are m1 in 8 sessions — because a one-click action with an eight-session reach has to say so somewhere.
Fallback
Where there is no full name to offer (a browser session, or an account called admin), the item degrades to That’s Me… with an ellipsis, asks once, and never asks again.

Two reasons this earns first place over any matching algorithm. It is the only identity claim in the product with a perfect source — the person asserting it is the person. And it is the most reused: a freelancer's own name is in every study they will ever run, so one click here is the last time the question needs asking, in this project or any other.

The item sits above Set Name, against the usual instinct to lead with the generic verb, because it is both the more common answer for an m code and the cheaper one. On a p code it does not appear at all.

3 · Still step 1 — the right person, spelled wrong

Session 7. p7 is Michel Hurlly, which is what Whisper heard. His name is Mickael Hurley, and the researcher knows that for certain because she recruited him. Nothing about his identity is in doubt.

contextual menu · step 1 proposed
anchor p7 Michel Hurlly
after committing the new spelling proposed

Fix the spelling in the transcripts too?

Michel Hurlly appears 6 times in the words of session 7 — where he says his own name, and where the moderator says it back.

Michel Hurlly6 places — becomes — Mickael Hurley

Quotes already pulled out keep the old spelling until you analyse again.

Commentary
the most common name edit there is, and the only one with ground truth
Why it is its own case
Not unknown, not misidentified — the right person, mis-transcribed. It is the safest edit in the whole vocabulary: no words move, no identity changes, nothing merges. And it is the most frequent, because a speech model mangles proper nouns on every raw-audio study while a platform transcript, which reads the name off an account, gets it right.
Why the researcher wins every time
She recruited him. The screener has the spelling. This is the one class where the human is not probably right but definitely right, so the interaction should cost nothing and ask nothing.
What only a rename can offer
The old string is known-wrong, so it can be found exactly. Setting a name on an empty field has nothing to search for; changing one does. That is the rule: rename offers propagation, naming does not.

The label and the words are two different places, and today only the label is fixable. The transcript body still says "Michel Hurlly" wherever anyone said it aloud — so the report shows the corrected name at the top of a quote whose text spells it wrong. Nobody would design that; it is just what happens when a name field and a transcript are edited by different features. The sheet is the cheapest possible bridge, and it is a scoped exact-string replace, not free-form transcript editing.

Neither button is destructive and neither is Cancel — "Just the Label" is a real answer, not a retreat, and it is what a researcher picks when the participant genuinely said a different name aloud. Escape takes the same path.

A name belongs to a code, and a code has one name. Correcting the spelling here corrects it in session 7, in every quote, in the participant table and in the export — there is no such thing as p7 being spelled one way here and another way there, and the sheet does not offer a scope because there is no scope to offer. That invariant is load-bearing well beyond spelling: it is what makes the moderator renumber in bench 9 forced rather than chosen.

The bigger version of this is a recruitment list. The researcher knew the spelling before the session existed — the names were in a screener days earlier. An imported roster would let the app match what it heard against names it was given, rather than guessing spellings from audio, and would collapse this whole class. Out of scope here, worth noting as the thing steps 1–2 are a manual stand-in for.

4 · Still step 1 — "Jane is an observer, not a participant"

Back to session 4. Jane Smith sat in to watch, said about two hundred words, and the pipeline classified her as a participant — so her side comments were mined for quotes.

contextual menu · Role submenu proposed
anchor p4 Jane Smith
Commentary
easy to do, and it does less than it looks

"in Session 4" is in the item text, not implied. Role is a fact about a person in a session, not about a person — the database has always modelled it that way, and the edge case is real: the same colleague can observe one session and moderate the next. Naming the scope in the item costs four words and prevents the reasonable assumption that this is a global switch. At a person-level anchor the same item reads Role in All 12 Sessions, and where the roles genuinely differ it says so rather than flattening them.

What it does not do: retract the six quotes already pulled from Jane's asides. Role governs who is eligible for quote extraction, and extraction has already run. Changing a label cannot un-ask a question that was already answered — so the surface offers Analyse again and lets the researcher decide, rather than silently spending a cloud call and discarding edits.

And it changes who is named in an export — today, by accident. Anonymising blanks names whose code starts with p and keeps m and o. Jane as a participant is blanked; Jane as an observer is named. One three-item submenu quietly moves a colleague across the anonymisation line, in the direction of more disclosure, with nothing on screen saying so. That is Part 5's problem, and it is live in shipped code.

5 · Two Marys, and one Mary twice — none of it a defect

The ordinary participant cast of a twelve-session study. p3 is Mary. p4 is Marrian. p5 is a different Mary. p6 is the first Mary again, back to talk about another part of the journey. All four are fine and reasonable, and only one of them needs the researcher to do anything.

People · project scope proposed
Name
Role
Sessions
Words
Quotes
p3
Mary A.Mary Adeyemi · ward sister
Participant
4,120
9
p4
MarrianMarrian Boateng · healthcare assistant
Participant
3,540
6
p5
Mary O.Mary Okafor · night charge nurse
Participant
5,010
11
p6
Mary Heard once. Mary A. and Mary O. are both in this study.
Participant
2,880
5
the same shape in the observer namespace proposed
Name
Role
Sessions
Words
Quotes
o1
Jane SmithProduct manager, Meridian Health
Observer
180
o2
Jane SmithProduct manager, Meridian Health
Observer
210
Same name in both sessions, and both were read from the meeting files.
Commentary
three rows need nothing; the fourth needs one pick
Same name, different people
p3 and p5 are two Marys who have never met. Correct as they stand, and the app proposes nothing — name equality is not evidence at any altitude, including inside one study. The only work is telling them apart on screen, which the shipped short-name collision handling already does: Mary A. and Mary O.
Same person, two codes
p3 and p6 are one Mary, back for a second session on another part of the journey. Normal, not a defect. Participant codes are numbered across the study so they never collide — which means one human returning simply holds two of them.
The only action on this screen
Naming p6 by picking the Mary who is already in the study. The codes join as a consequence. One pick.

This is the common participant case, and it should be almost invisible. The temptation is to build matching — two "Mary"s in one study, surely worth a suggestion? No: the app cannot tell these three apart and would be wrong a third of the time. What it can do is put the ambiguity where the researcher will see it, name both candidates, and let the person who was in the room answer in one click.

Note what the contested row does not say. Not a confidence score, not "possible duplicate", not a merge proposal. Just the two names that are already in this study and the word Which? — because the researcher knows, and the app does not.

The second frame is the same shape one namespace over. Moderator and observer codes fragment as well as collide, for a dull reason: the number comes from within-session ordering, which has nothing to do with identity. Jane observes session 1 alone and is o1; she observes session 3 alongside Tom, who happens to speak first, and she is o2. Same person, two codes, and nothing about her changed.

So all three namespaces need the join, and only m/o need the split — and every one of those repairs is the same gesture: name the person in front of you. Here the app offers, because both names are platform records rather than guesses; a Mary heard once gets the silent Which? treatment instead. Offering is not merging, and the strength of the evidence decides which you get.

6 · The common attribution error, and the picker that serves it

Not "that paragraph" — all of it. Everything the diariser attributed to p3 in this transcript is the moderator talking. One statement, not forty corrections. And the person you are attributing to may be someone the system has never noticed.

speaker entry · whole-transcript remap proposed
anchor p3 Participant session 3 · 41 turns
New Observer — create and name in one step proposed

New observer in session 3

They will be o2 in this session.

Commentary
one statement replaces forty corrections
The grain that matters
Not a turn. All 41 turns at once, because when the diariser is wrong about a speaker it is usually wrong about all of them. Cheap too: one bulk update inside a session — no word-timing division, no split, no merge, no quote cascade.
Why it is an identity item, not a Turn item
It moves words, but what the researcher asserted is who p3 is — that the diariser invented a speaker who is really the moderator. The families are told apart by what is asserted, not by what the database ends up doing. Same shape as a rename causing a renumber.
Grouping is the role
Picking inside Observers sets an o code; picking New Moderator… mints an m. Role and identity are chosen in one gesture rather than two, and the groups run in p/m/o order — the order codes are read in everywhere else — rather than by guessed frequency.

The gap this closes: a person the system never noticed. Diarisation merges two people into one code and the second has no code at all, so there is nobody to reattribute to. This is the exact inverse of Not a Speaker, and without a create path the picker simply cannot express the correction. New Participant… mints the code on the spot.

Create and name are one action. A nameless p7 is a second chore, and the researcher knows the name at precisely the moment they are creating them. For a new moderator or observer the field offers the bank, because a new colleague is usually a known colleague; for a new participant it is blank, because they usually are new. Note this is the one place the two lists meet — the cast (who could this speech belong to, in this session) and the bank (who is this person, across studies) are otherwise different questions and different lists.

The whole cast fits without scrolling because the cast is small: a dozen participants, three or four colleagues. At that scale a grouped list is faster than any search field, and a search field would be a confession that the list is too long.

7 · They talk over each other, and the quote is credited to the wrong one

Session 3, around 18:40. Sarah (p3) and Jane (p4) overlap for eight seconds. The diariser cut the boundary in the wrong place, and one of the study's better quotes now carries the wrong name.

contextual menu on a quote card proposed
anchor “…by then the list has already moved on, so you’re handing over something that isn’t true any more.” p4
after picking m1 — no dialog proposed

Now credited to Martin (m1). This lens only shows participant quotes. Show in transcript · Undo

Martin has 5 more quotes in this session. Those too

Commentary
cheap, and the highest-consequence attribution fix there is
Why it outranks fixing the turn
A quote is published. It goes in the report, the spreadsheet, the Miro board, the deck. A transcript is a working document nobody outside the team reads. A slightly-wrong transcript is tolerable; a quote credited to the wrong participant is a factual error in a deliverable.
Why it is cheap
One field on one quote. No segment surgery, no word-timing division, no per-speaker stats recompute, and crucially no cascade — which is the unsolved part of doing it at segment level. The speaker-editing design already reaches this conclusion and calls it "recommendation B, simplest first".
Why crosstalk may never be fixable upstream
Two voices genuinely occupy the same three seconds. No boundary is correct. So the honest design does not promise a perfect transcript — it gives the researcher a way to make the output right regardless.

Quotes exist only for participants, so the current value is always a p code and there is no such thing as a moderator’s quote. That makes the two halves of this list mean different things. Picking another participant re-credits — genuine crosstalk, the words are a finding, credited wrongly. Picking a moderator or observer says this is not a participant quote at all — the speech is re-attributed, and the card leaves the lens because the lens only shows participant quotes. The group header says which is which at the point of action.

That second half is the everyday repair from bench 11 arriving on a different surface: an observer mis-filed as a participant had their asides mined as findings, and this is where a researcher meets the consequence — one odd quote at a time, while reading.

The card disappearing is a consequence, not the action. A quote is a view onto speech, not a copy of it — so re-attributing it moves the words, and going to session 3 afterwards shows them as the moderator’s. Had only the card changed, the two surfaces would disagree: the quote gone from here, the transcript still showing p4 saying it. Hence Show in transcript sitting next to Undo — the quote was not destroyed, it moved to where it belongs, and the researcher should be able to go and see that.

And nothing is withdrawn. There is no flag, no reason field, no pile it moves to. The Quotes lens shows participant quotes; re-attribute the speech to m1 and the card is simply no longer in that set — reversible for free, because putting the speech back puts the card back.

Two orthogonal axes, and keeping them apart is the whole point. Membership — is this a participant quote? — is a fact about who spoke, derived from speaker identity, with no state of its own. Emphasis — star promotes, hide demotes — is a judgement about evidence quality that the researcher makes deliberately and owns. Star and hide are one spectrum: picking and choosing the best evidence. Speaker identity is not on that spectrum at all. Which is why the hidden count stays meaningful — it counts judgements, not facts — and why a correction must never be expressed through it.

Note the sentence in the frame above avoids the word hidden. Borrowing the curation verb for a membership fact is the exact confusion this whole distinction exists to prevent.

And then the pattern this design uses everywhere: do the narrow thing, then offer the wide one in a sentence. One quote is re-attributed, because that is what was asked. The app, now holding strong evidence, says so and notes the five quotes still credited to p4 — inviting review, claiming nothing, and never performing the sweep uninvited. It is how a reading surface can default to the thing under the cursor while the underlying error is usually study-wide.

Trim and Hide sit in the same menu deliberately. When speech overlaps, the extracted text is often a genuine mash of two voices, and re-crediting all of it to one person swaps one wrong attribution for another. The real repair is to trim to the part one person actually said and then re-credit — or, when it is unsalvageable, to hide it. Both already ship; the menu just puts the three answers in one place, in the order a researcher tries them.

Note the noun. This Quote Is — a unit of speech, so the fix moves words. Role, Change Name, Same Person As — units of identity, so they only relabel. The rule generalises from the earlier one: the item names the object it acts on, and speech nouns are the ones that move things.

8 · The transcript, where all three grains meet — and only one is expensive

Right-click the badge on a turn. Everything attribution can do is reachable from here, at three different reaches, and the item text says which is which.

transcript · segment badge proposed
anchor
18:42 p4 …and then you’d have to go back to the list and start again, which by that point has moved on.
Commentary
the lexical rule, legible in one screenshot

Two items say Turn and they are the two that move words to a different speaker. One says All of p4’s Turns Are — the whole-speaker sweep, named for the speech it moves and classified attribution, where its effect belongs. (An earlier draft spelled it p4 Is Actually — identity wording on a 41-turn word-move, distinguishable from the pure relabel m1 Is only by an adverb; the review killed it, so the rule now holds with no exceptions.) The rest relabel and move nothing. A researcher does not have to be taught this; they can read it off the menu.

Reach, top to bottom
This Turn Is — the thing under the cursor. All of p4’s Turns Are — all 41 turns in this transcript. The wider one is deliberately one group down: this is a reading surface, so the narrow act is the default and the sweep is available with its scope written into its name.
With a selection
The first item reads These 4 Turns Are. Selection carries the scope, which is the standard text-editor idiom and introduces no new concept — shift-click a run of misattributed turns, then fix them together.
Cost, which is not evenly spread
This Turn Is and All of p4’s Turns Are are bulk updates — cheap. Only Split and Merge need segment surgery: word-timing division, index management, a quote cascade. They are the rare grain, and they are the reason step 5 of the sequence is the largest item in it.

One exception worth carrying forward: when a split is needed because a quote sits inside a longer turn, the quote’s own timecode range supplies the split point. That case needs no cursor placement and no new interaction, which makes the flavour of turn surgery that arrives from a reading surface markedly cheaper than the general one drawn here.

9 · "The moderator in session 9 is Mike, not Martin"

The tail case — and the one this document first drew as a three-button sheet. It is not. One moderator runs about 95% of studies, so m1 meaning the same person everywhere is usually right; where it is not, the researcher fixes it the same way they fix everything else, by naming the person in front of them.

session 9 · the same menu as bench 2 proposed
anchor m1 Martin Storey session 9
immediately after — no dialog proposed

Mike is now m2 in session 9. Martin stays m1 in sessions 1–8. Undo

Name
Role
Sessions
Words
Quotes
m1
Martin StoreyYou
Moderator
1–8
12,100
m2
Mike Alvarez
Moderator
1,640
Commentary
the dialog was the wrong answer — this is a rename with a consequence
Drawn before
A sheet titled "Separate m1 into two people?" with three buttons, a chain diagram and a nested question. It priced a tail case as though it were a feature.
What the researcher does
Opens session 9, says the moderator here is Mike, carries on. They never think "separate m1" — they think about the person in front of them.
What the app does
Notices m1 is Martin elsewhere, concludes this is a different person, renumbers Mike to m2, and says so in one sentence with an Undo. No confirmation, because the house rule is that nothing confirms and everything ⌘Z’s.

Renumbering is a consequence of naming, not a verb. That is the whole of decision 1, and it is why the same menu from bench 2 absorbs the tail case without growing an item. Three things that looked structural — joining two participant codes, separating a collided moderator, linking across studies — are all bookkeeping behind an act the researcher has already performed, provided naming is built as an identity assertion rather than a string edit.

The heavy sheet survives for one path only: noticing at study level, in the People lens, that a code covers two names — where there is no single session to open and no rename in flight. Secondary door, not the primary one.

10 · "Michael J. Hurley-Okonkwo" is fine on the invoice. In the study he is Mike.

Teams writes the account's formal name onto every turn — middle initials, double-barrelled surnames, the occasional title. The research team says Mike, and Mike is what belongs beside a quote.

Change Name — sheet proposed

p4

Called Appears beside every quote, and in the report. Full name From the Teams transcript. Kept for your records; not shown with quotes.
Commentary
two fields exist; one has an editor; the other is a tooltip
The model is already right
full_name and short_name have been on PersonEditable from the start, and suggest_short_names() derives the second from the first with real care — honorific stripping, family-name-first detection, 337 surnames, and collision handling that produces "Sarah J." and "Sarah K." when two participants share a first name.
The UI is not
The pencil in the Sessions grid edits short_name and displays short_name || name. full_name appears only as a hover tooltip, and only when it differs. Nothing in the SPA can edit a full name at all.
So the user's case is half-served
You can already type "Mike" — but you cannot see that you are editing the display name rather than the record, and if Teams supplied a wrong or over-formal full name it is uncorrectable. The field is not inert: it is what the participant table shows, and it is in the export.

The sheet's job is almost entirely teaching by consequence. Two fields, each with one line saying where it shows up. "Called" first, because it is the one being changed nine times out of ten and the one with visible effect. Derivation still runs — leave "Called" empty and it fills from the full name — but it is now visibly a default rather than an invisible one.

This is also the quiet answer to the anonymisation question. A study that never records full names and works entirely in short ones has less to strip on the way out. Making the distinction visible lets a researcher choose that, which is a governance decision the product currently makes for them by hiding one of the fields.

11 · Observers are systematically mis-filed, and correcting it makes the export worse

Nobody introduces the observer. They sit quietly for twenty minutes, then say one useful thing — and when they do, they sound exactly like a participant.

sweep line, People or Sessions proposed

2 people barely spoke and were never asked a question. Were they observing? Take a look

Name
Role
Sessions
Words
Quotes
p3
Sarah ChenWard sister, acute medicine
Participant
4,120
9
p4
Jane Smith 210 words, first spoke at 31:40, never asked a question.
Participant
210
6
Commentary
a structural bias, and a perverse incentive on top of it

Observers cannot be classified by the machinery as built, and they fall through to participant by construction. Role identification samples roughly the first five minutes (seg.start_time > 300), so anyone who first speaks at 31:40 is not in the sample at all. The heuristic that covers the rest scores question ratio and moderator phrases — an observer fires neither. And the code assignment maps PARTICIPANT and UNKNOWN to a p code, so "could not tell" and "is a participant" produce identical output. The observer is not unlucky; they are the default.

The signals that would catch them are computable and already stored: very low word count, a late first utterance, and never being the addressee of a moderator question. None needs a model. One absent-when-zero line is enough — it costs no column width, it is a single translated string, and it targets a bias rather than nagging about every speaker.

Now the perverse part. Correcting Jane from participant to observer moves her across the anonymisation line, towards disclosure: the export blanks names whose code starts with p and preserves m and o. As a mis-filed participant her name is stripped from the client's leave-behind. Correctly filed as an observer, it is published. The most common role error currently produces the safer export, and fixing it produces the riskier one — which means the sweep above cannot ship before Part 5's second decision, or it will do net harm while looking like a correctness feature.

12 · The bank — "oh yes, that m1 is Steve"

A team of three or four researchers moderates everything. Their names are typed once, ever. After that, naming a moderator is a pick, not a keystroke.

contextual menu · the bank proposed
anchor m1 Moderator
Commentary
the keystone — it turns the hardest step into a by-product of the easiest
What the bank is
Not a new store. Person rows are already instance-scoped — no project_id, deliberately, with a docstring saying merging is a future human-driven action. The bank is a query over rows that already exist, ordered by how often each has been used.
Why moderators and not everyone
The recurrence is wildly lopsided. Three or four researchers appear in every study; a participant appears once and never again. So the bank is short, its hit rate is high, and it earns its place for exactly the population the user described.
The part that changes the plan
Picking a name from the bank is a link, not a copy. Choosing "Steve Nakamura" asserts that this m1 is the Steve who already exists — which is precisely the cross-project identity link that was step 6. Obtained here for free, at step 1, as a side effect of not retyping.

Prevention beats reconciliation. The linking feature as designed is a reconciliation engine: import everything, generate duplicates, then match names within a folder, propose, confirm, and handle transitive chains. The bank stops the duplicates being created. What is left for a links table is a back-fill for people named before it existed — a much smaller and much less dangerous feature, and one with no matching algorithm in it at all.

"Someone New…" has to be the visible escape, and the safe default. The failure mode is a researcher picking "Rachel Okonjo" for a different Rachel, which silently welds two people together. So the bank never pre-selects, never auto-completes on a keystroke, and the study counts beside each name are there to make "which Rachel" answerable in the menu itself. Cross-client picks still deserve the confirmation from the linking work — the bank changes how a link is created, not whether crossing a client boundary is worth a sentence.

"Me" sits above the separator and outside the team group, because it is not a person you might be confusing with another person. It is also the row that seeds an empty bank: the first time anyone uses this, NSFullUserName() is the only entry there is.

One menu, four surfaces What "adapts to the context" means, concretely.
SurfaceWhat you clickedScope it carriesWhat it offers
Transcript
a segment’s badge
one turn, by one speakerthis turn · this speaker in this transcript This Turn Is ▸ · These N Turns Are ▸ · All of p3’s Turns Are ▸ · Split Turn Here · Merge with Turn Above · name · role · Not a Speaker
Quote cardone published extractthis quote This Quote Is ▸ · Trim Quote… · Hide Quote · Show in Transcript
Sessions grid
a speaker entry
this speaker in this sessionthis session Set Name… / m1 Is ▸ / That’s Me (Name) · Role in Session 4 ▸ · All of p3’s Turns Are ▸ · Not a Speaker
People lens
a row · project scope
this person in this studythe whole study name · Role in All 12 Sessions ▸ · Separate… (when contested) · Same Person As ▸ · Part of My Research Team
People lens
a row · folder or everyone
this person across studiesacross projects name · Same Person As ▸ · Not the Same Person · Unlink… · Part of My Research Team
Commentary

Four rules generate every cell, which is what makes this one menu rather than four that will drift. (1) Offer what is meaningful at that anchor and omit what is not — a Turn item has no turn in scope on a sessions row, so it is absent, not greyed; a contextual menu shows what applies, a menu bar keeps its shape. (2) One verb at many anchors, with the scope in the label — Role in Session 4 and Role in All 12 Sessions are the same verb one altitude apart, and the researcher cannot see the anchor. (3) State picks within a zone: unnamed offers Set Name…, a guess offers Confirm, a contested code offers Separate…. (4) Never offer a no-op.

Written as a function of (where you clicked, what state it is in), a new verb lands everywhere it is meaningful at once, and a surface added later inherits the whole vocabulary instead of re-implementing a subset of it.

And the subtler half: default reach follows the working mode, not only the anchor. The anchor bounds what is possible; the surface says what the researcher is probably doing.

Where you areWhat you are doingDefault reachWorked example
Sessions grid · People lenssweeping — housekeeping, row after row, expecting ripplethe widest meaningful scope"o2 in s3 is the same Jane as o1 in s1" — joins the codes and ripples into every quote in that transcript
Transcript · quote cardreading — analysis, in the flow of a conversationthe thing under the cursor"that one quote is Sarah" — this quote, nothing else

The other reach stays reachable and is never the default, and the asymmetry in how is deliberate. Widening from a narrow surface is a menu item — inside the transcript, All of p4’s Turns Are sits one group below This Turn Is. Narrowing from a wide surface is navigation — you do not fix one quote from the People lens, you go to the quote. A row that stands for twelve sessions has no business offering an action that touches one paragraph.

One pattern, five benches How a reading surface defaults narrow while the underlying error is usually study-wide.
BenchThe narrow thing, doneThe wide thing, offered in a sentence
3The spelling is corrected on the labelMichel Hurlly appears 6 times in the words of session 7.” — Fix All 6
4Jane’s role is changed in session 4“Quotes were pulled out using the old roles.” — Analyse again
7One quote is re-credited to the moderator“Martin has 5 more quotes in this session.” — Those too
9Session 9’s moderator is named MikeMike is now m2. Martin stays m1 in sessions 1–8.” — Undo
112 people barely spoke and were never asked a question. Were they observing?”
Commentary

Do the narrow thing, then offer the wide one in a sentence. It arrived independently in five benches before anyone named it, which is the usual sign that a pattern is real rather than imposed. It is also the thing that resolves the tension between the two defaults above: a reading surface can act on the thing under the cursor, and still get the researcher to the sweep, without ever performing the sweep uninvited.

Three properties make it work. The app speaks only after the researcher has acted, so it is never guessing at intent. It quotes a count — six occurrences, five quotes, eight sessions — because a number is checkable where an adjective is not. And the offer is one link, declinable by ignoring it: no dialog, no modal, nothing to dismiss. Bench 11 is the degenerate case with no preceding act at all, which is why it has to be absent when its count is zero — an unprompted sentence has to earn its place every time it appears.

Trivial, or merely possible Every bench above, priced by what it costs the researcher.
BenchThe caseHow commonWhat it costs
1Name an unnamed participantevery sessiontype a name
2"That moderator is me"every studyone click, no typing
3Fix a mis-transcribed spellingevery raw-audio studytype it, then one choice about the transcript
4Participant → observerthe 95% extra personone submenu pick
5Two Marys, one Mary twice, and Jane’s two observer codesordinary cast of any studynothing for most rows; one pick for the rest
6A whole speaker in a transcript is someone elsethe common attribution errorone pick from a grouped list
7Crosstalk — re-credit one quote, or move it off the evidencea few times a studyone submenu pick
8One turn, or a split mid-turnrare — raw audio onlyone pick; a split needs a cursor placed
9A second moderatortail — ~5% of studiesone pick, plus a sentence telling you what changed
10Formal name vs the name we useevery platform-transcript studytwo fields, once per person
11Spotting mis-filed observersevery study with anyone extraread one line, then one pick each
12Naming a colleague from the bankevery study, after the firstone pick, and never again
Commentary

Nothing on this page costs more than one action and a sentence, and the two that cost the most — a second moderator, a mid-turn split — are the two that happen least. That is the whole test. A design that made the second-moderator case elegant at the price of a dialog in the naming path would be a worse design, however well the dialog read.

Several rows cost nothing at all: two Marys who are different people, a returning participant seen for what she is, a moderator who is the same person in every session. Those are the rows the product gets right by going with the typical case rather than by reasoning about it — and they are the majority of every study.

Part 4 — the vocabulary, in one table Every confirmed scenario, its verb, the anchor it is reached from, and the step it lands in.
FamilyThe researcher's sentenceMenu itemAnchorStep
identity"p4 is a human called Jane Smith."Set Name…any person reference1
identity"m1 is me."That's Me — Martin Storeyan m/o code1
identity"That m1 is Steve."m1 Is ▸ (the bank)an m/o code1
identity"Jane is an observer, not a participant."Role in Session 4 ▸speaker in a session1
identity"Michel Hurlly is Mickael Hurley."Change Name… + propagateany person reference2
identity"He's Michael J. Hurley-Okonkwo, but call him Mike."Change Name… (two fields)any person reference2
identity"Yes, that guess is right."Confirm "Danny"a guessed name2
identity"That email address should not be in the name."Clear Nameany person reference2
attribution"They talked over each other — that quote was Sarah."This Quote Is ▸ → another pa quote card3
attribution"That quote is the moderator talking — not a participant quote."This Quote Is ▸ → an m/o: moves the words; the card falls out of the lensa quote card3
attribution"Everything attributed to p3 here is the moderator." / "Speaker A and Speaker C are one nurse."All of p3's Turns Are ▸speaker entry · segment badge4
identity"The moderator in session 9 is Mike, not Martin."m1 Is ▸renumbers to m2speaker entry, in that session4
identity"o2 in s3 is the same Jane as o1 in s1." / "p6 is the same Mary as p3."name it from the bank — joins the codesspeaker entry · People row4
attribution"That turn was Sarah, not Jane."This Turn Is ▸segment badge5
attribution"Those four turns were all Sarah."These 4 Turns Are ▸segment selection5
identity"That is not a person — it is the TV."Not a Speakerspeaker entry · segment badge4
attribution"Two people are in that one paragraph."Split Turn Here / Merge with Turn Abovesegment badge5 (the expensive one)
identity"This is the same ward sister from round 1."Same Person As ▸person row, folder scopeback-fill
identity"Same name, different people."Not the Same Personperson row, folder scopeback-fill
Part 5 — one decision settled, one still owed Neither was a coding problem. Both are product calls.
Commentary — decision 1, settled: identity lives above codes
settled — the question was the wrong one

Does a code name a person or a slot? Neither. Sessions are s1, speakers are p1/m1/o1, a code is a globally-numbered speaker slot, and identity is a layer above it. Neither namespace needs re-specifying, because each is already right for its own common case — they just fail in different directions:

So all three namespaces need the join and only m/o need the split — and every one of those repairs is the same gesture: name the person in front of you. Bench 9 renumbers Mike to m2; bench 5 joins Jane’s two observer codes and Mary’s two participant codes.

The renumber is not a design choice — it is the only representable outcome. A name belongs to a code and a code has one name everywhere, so "session 9’s moderator is Mike" cannot mean "m1 is Mike here and Martin elsewhere". That sentence has nowhere to live. The same reading disposes of the alternative: the m1 collision is not a bug in the name model — it is the name model working correctly on a code that names two people. Re-keying by (session, code) would have fixed the symptom by breaking the invariant, letting one code carry two names and quietly making "what is p4 called?" a question with more than one answer.

Commentary — decision 2: what actually decides whose name survives an export?

Today it is the letter at the front of the code. _anonymise_data blanks p* and preserves m* and o*. Three consequences, all live:

The fix is to move the decision up an altitude, where it has always belonged. Per-session role governs quote extraction — correctly, because someone interviewed in session 9 should have quotes from session 9. Person-level membership governs naming: one value per human, one answer per export. In the menu that is a single checkmark item at a person anchor — Part of My Research Team — and the export reads that instead of the prefix. It is also what populates the bank's "Your team" group, so one flag does two jobs.

Part 6 — what the affordance owes Four obligations that come with putting a menu on a person.
Commentary
Right-click is one door
The sidebar rename precedent settled this: a gesture ships alongside a menu-bar path, a keyboard path, and a direct-manipulation path, because the gesture is undiscoverable and the others are teachable. Person actions should reach the same four ways — the contextual menu, a menu-bar Edit or Session item, a keystroke on the selected row, and the pencil that already ships. The menu is where the vocabulary lives; it is not the only way in.
Nothing here confirms; everything ⌘Z's
That is the house principle, and this surface cannot honour it today. NSUndoManager is used nowhere, the undo-state bridge channel is dead on both ends, and bridge.ts hard-codes canUndo: false. Every action in Part 4 is a mutation a researcher will want back — and step 1's actions are the ones they will perform thirty times in an afternoon. An affordance this cheap to fire needs an undo before it needs more verbs.
The SPA has no contextual menus at all
Not one onContextMenu handler exists in frontend/src. So this is a new mechanism, not a restyle, and inside the WKWebView it means suppressing the system menu and rebuilding what it gave away — keyboard traversal, type-select, Escape, focus return, and the accessibility tree. A half-built HTML menu is worse than the pencil that ships. The alternative worth pricing: let the web view report the anchor over the bridge and have AppKit raise a real NSMenu, which is free, correct, and native — at the cost of the CLI browser path getting nothing.
Say the scope, in the item
Role in Session 4, Role in All 12 Sessions, All of p3's Turns. The anchor determines the scope, and the researcher cannot see the anchor. Four words in the item text is the whole mitigation, and it is cheaper than any confirmation dialog it replaces.

The speculative register — cases neither confirmed nor dismissed — lives in the PRD, docs/design-people.md §F, where it can be triaged in writing rather than redrawn here.