---
name: diary-study-workaround-board
description: "Turn self recorded diary study entries into an evidence board of workarounds and settings with Vivu: participant, entry, time, frame and quote per row. Use when synthesizing a diary study."
---Diary study workaround board
This skill takes the entries from one round of an unmoderated diary study (participants' self recorded phone videos, usually one to three minutes each, with the participant ID in the file name) and four to six things the researcher wants found, and returns evidence_board.csv: one row per moment where a participant uses a workaround (a paper list, a sticky note, a calculator or a second device) or records from a setting that matters (a car, a kitchen, a desk), or says something about the workflow (a step that takes too long, a tool they gave up on). Each row has the category, the participant ID taken from the file name, the entry file, the start and end time, a scene description written from the frames, the frame path, the participant's words from a transcript when there is one, a privacy flag and an empty column for the insight. Claude prices the indexing against the user's Vivu plan, uploads the consented entries to a private Vivu project, runs one search per category, checks every window against frames or the transcript, and only then writes the board.
The value is in what the entry shows and the transcript does not. Participants say "this is how I keep track" or "I just do it here", and what "this" and "here" are only exists in the picture: a handwritten list held up to the camera, a calculator next to the phone, the front seat of a car. Diary platforms' transcripts do not describe the picture, and many entries have no transcript at all. Vivu finds candidate moments by what is on screen and what is said. Small objects are where it is least reliable: in our test run it called a lined notepad sheet a sticky note and missed a list shown only in an entry's last seconds, and whether it tells an object in use from one lying in the background was not tested, so every window is checked against frames before it becomes a row, and rejected windows are counted.
When to use
Use when a UX researcher says "pull every workaround out of this week's diary entries", "find where participants use paper lists or sticky notes instead of the app", "which entries were recorded in the car", "find where people complain a step is too slow or say they gave up on a tool", or "build an evidence board from these 40 diary videos for synthesis". For one question about one entry, search Vivu directly. For moderated usability sessions or screen recordings, where the evidence is what happens on screen, use a usability issue skill instead; for sales or customer calls, use a call evidence skill. This skill does not score participants, summarize whole entries or identify people.
Working principles
- Report measured numbers, not estimates. When a number is an estimate, say so.
- A moment becomes a row only after its frames show the object or setting (visual categories) or the transcript shows the words (spoken categories). Vivu's results are candidates until then; rejected candidates are counted and kept in rejected.csv.
- Stop and tell the user when a required capability, consent or file is missing (no ffmpeg, no write access in Vivu, entries without cloud processing consent). Do not guess around it.
- Ask the user before anything that is expensive to redo or that acts on their behalf: the indexing cost before upload, the sample rows before the full board, and a Notion page before it is created.
- Describe what the entry shows and says. Never judge the participant, and never infer who someone is from a face or a voice; the participant ID comes from the file name only.
What you need before starting
Check each item at the start of the run and tell the user plainly what is missing.
| Requirement | Why | How to check |
|---|---|---|
| Vivu connector with write access | create a private project, open its upload page, search | vivu_get_account shows can_create_projects: true (tool names may carry a server prefix). A write call failing with "has not granted vivu.write" means the connection is read only; the user reconnects Vivu and allows write access |
| A shell with ffmpeg and ffprobe on the machine that holds the entries | measure minutes, make contact sheets and frames | ffmpeg -version and ffprobe -version |
| The diary entries as local video files, with the participant ID in each file name | frames come from the local files; Vivu has no export tool; participant IDs come from file names | ls ENTRIES_FOLDER and show the user three file names with the ID pattern |
| The consent record for the study | only entries whose consent covers upload to a third party service may be indexed | the user confirms which participants are covered, or points to the consent sheet |
| Transcripts where the diary platform exports them (VTT, SRT or TXT next to each entry) | quotes for spoken categories come from them, never from Vivu's reason text | ls ENTRIES_FOLDER for transcript files; tell the user which entries have none |
| A browser that can open the Vivu upload page | the connector has no direct upload tool | the user opens the link, or Claude in Chrome is connected |
| Notion connector, only if the board should also go to a Notion page | Step 7 | open the parent page the user names |
The skill needs a shell and local files, so it runs in Claude Code on the user's computer (terminal or the Code tab of Claude Desktop). It downloads nothing from YouTube, so no residential IP is needed, and nothing is scheduled.
Inputs to collect
Ask for anything missing, most important first.
- The categories, four to six, each with a key and one line of what counts (for example paper_list: a handwritten list or notebook page the participant holds up, points at or writes in). Required. Aim for about half visual (objects, settings) and half spoken.
- The entries folder (ENTRIES_FOLDER). Required.
- The participant ID pattern in the file names. Default: the leading P plus digits (P07_day2_entry3.mp4 gives P07); confirm on three real names.
- Which participants are covered by consent for cloud processing. Required; entries outside it are left out.
- Whether to also create a Notion page. Default: no, evidence_board.csv only.
- The Vivu project name. Default: "Diary study STUDY_NAME round N", private, where STUDY_NAME is the study's short name.
Files and state
Keep everything in one working folder next to the entries:
diary-board/
categories.json category key, what counts, query, visual or spoken
entries.csv file, participant_id, minutes, consent ok, transcript file, vivu video id
results/ raw search results, one JSON file per category
sheets/ 0.5 second contact sheets of each window
frames/ one full frame per verified visual row
evidence_board.csv verified rows
rejected.csv windows that failed the check, with the reason
state.json uploaded files, searches run with job ids, every window with its verdict, rows written, Notion page created
state.json is updated after every step. A rerun reads it first: files already uploaded are not uploaded again, searches already run are not rerun, checked windows keep their verdicts, and a Notion page already created is never created twice, so an interrupted run resumes where it stopped and no moment is written twice.
Step 1: Check the Vivu connector and the setup
Goal: every row of What you need is confirmed before anything is uploaded.
- Call vivu_get_account. If the tool is missing, tell the user to add the Vivu connector in Claude (https://mcp.vivu.ai/mcp) and stop. If can_create_projects is not true, or a write call later fails with "has not granted vivu.write", ask the user to reconnect Vivu with write access and stop until they have.
- Run ffmpeg -version and ffprobe -version. List ENTRIES_FOLDER, show three file names and the participant ID each one yields, and note which entries have a transcript file next to them.
- If a Notion page is wanted, open the parent page the user names.
- Create the working folders; ffmpeg does not create an output folder:
mkdir -p diary-board/results diary-board/sheets diary-board/frames
Run every later command from inside diary-board/ (cd diary-board), with ENTRY_FILE as the path to the entry from there.
Done when vivu_get_account shows can_create_projects: true, ffmpeg and ffprobe print versions, the participant ID pattern is confirmed on three names, and the working folders exist.
Step 2: Confirm consent and write the categories
Goal: categories.json and entries.csv, with only consented entries in it.
- Ask the consent question in Compliance item 1 before anything else in this step. Leave out every entry whose participant is not covered, and every entry from a participant under 18.
- Write entries.csv: file, participant_id (from the file name with the confirmed pattern, never from a face or a voice), consent ok, transcript file or blank.
- Write each category as a query in plain words. Visual categories describe the object and what the participant does with it, because a list lying in the background is not a workaround; spoken categories describe what the participant says, not exact words. Use this shape:
| Field | Query template | Mode | maximum_results |
|---|---|---|---|
| OBJECT_KEY | the person holds up, points at or writes in OBJECT that they use to keep track of things | precise | 30 |
| SETTING_KEY | the person is recording the entry while SETTING | precise | 20 |
| SPOKEN_KEY | the person says SPOKEN_THING | precise | 20 |
OBJECT is the workaround in plain words (for example "a handwritten paper list, notebook or planner page"), SETTING the place ("sitting inside a car"), SPOKEN_THING the statement ("they gave up on or stopped using an app, a planner or a system because it did not stick"). All searches are precise because rows need time ranges; a fast search only returns whole files, and every entry is short enough that there is no file choice left for fast to make. maximum_results is 30 for the main object category because a typical round has fewer moments of one workaround than that; it is also the ceiling on what comes back, so a list that stops at exactly that number means the cap was hit: raise it and rerun. 4. Show the categories, the queries and entries.csv, and wait for a yes.
Done when the user has confirmed consent and approved categories.json and entries.csv.
Step 3: Price the indexing and get approval
Goal: the user sees the cost before anything is uploaded.
- Measure each entry and sum the minutes:
ffprobe -v error -show_entries format=duration -of csv=p=0 ENTRY_FILE
- Call vivu_get_usage for the plan and what remains this month.
- Estimate search credits: one precise search per category plus one rewording per category in case the first page is not clean. A precise search uses 5 credits in total. Label the total as an estimate.
- Show one table and wait for approval:
| This round | Remaining on the plan | |
|---|---|---|
| Index minutes | measured sum | from vivu_get_usage |
| Search credits | estimate | from vivu_get_usage |
For reference, the Free plan has 20 index minutes a month and 50 search credits a month; Premium is $30 a month with 180 index minutes and 500 search credits. Forty two minute entries fit Premium, not Free. If the round does not fit, offer these levers in order: index the participants whose entries the researcher is synthesizing first, split the round across months, and only then a larger plan. Never drop an entry the user asked for without saying so.
Done when the user has approved the minutes and the credit estimate.
Step 4: Upload and index
Goal: every consented entry is ready in a private Vivu project.
- Call vivu_list_projects and reuse the project named in the inputs if it exists. Otherwise call vivu_create_project with that name and visibility "private". Diary entries show participants' homes and families; the default visibility is the whole organization.
- Call vivu_open_upload_page with the project ID right before the upload. The link expires in 180 seconds and is a sign in link, so never paste it into a message or file. Give it to the user to open in their own browser and choose the files from entries.csv, or attach the files with a browser tool that can upload local files. Claude in Chrome accepts at most 10 MB per upload call; larger entries are added by the user in the Vivu web app. Never split or recompress an entry to fit.
- Poll vivu_list_videos about every 30 seconds until every file shows ready. Match each video back to entries.csv by file name; Vivu turns spaces and punctuation in names into "_", so compare names with those characters replaced. Record the video IDs in entries.csv and state.json.
Done when vivu_list_videos shows every file in entries.csv as ready.
Step 5: Search each category and check every window
Goal: for each category, a list of checked moments with rejected windows counted.
Run each category's query with vivu_search_videos (project_id, query, mode "precise", maximum_results from categories.json). It returns a job ID. Call vivu_get_search_results until complete is true; each status call can wait up to 45 seconds, so a pending search is not a stalled one. Save each completed result as results/CATEGORY_KEY.json. Show the result page link in the live reply only; it expires after four hours, so it never goes into the board, Notion or any file.
Vivu returns a time range that contains the moment, not an exact frame, and the reason text describes what Vivu thinks it saw; it can name an object that is not there. Check every window:
- Visual categories: make a contact sheet of the window at two frames a second (START is start_ms / 1000, DURATION is (end_ms - start_ms) / 1000, ROWS is DURATION x 2 / 10 rounded up, ENTRY is the entry file name without its extension, for example P07_day2_entry3):
ffmpeg -v error -ss START -t DURATION -i ENTRY_FILE -vf "fps=2,scale=240:-2,tile=10xROWS" -frames:v 1 sheets/CATEGORY_KEY_ENTRY_START.png
Look at the sheet. The window is real only if a cell shows the object in use (held up, pointed at, written in, stuck where the participant works) or the setting itself. Reject it when the object is only in the background, when it is something similar (a typed list on a tablet, a notepad page called a sticky note), or when nothing matches. When a cell shows it, extract one full frame at that cell's time (SECONDS is START plus the cell's index divided by 2; cells count from 0, left to right, top to bottom) and look at it before writing the row:
ffmpeg -v error -ss SECONDS -i ENTRY_FILE -frames:v 1 -q:v 3 frames/CATEGORY_KEY_ENTRY_SECONDS.png
If the full frame does not show what the cell showed, step forward half a second and try again. Keep the quotes around the filter; some shells treat the comma list as a pattern otherwise. 2. Spoken categories: read the transcript lines inside the window. The window is real only if the participant says it there. When an entry has no transcript, read vivu_get_video_summary with include_segments, start_ms and end_ms around the window as a second signal, keep the row as a candidate with quote NOT VERIFIED, and tell the user to listen to that stretch. 3. Run spoken categories with two wordings and merge the windows (for gave_up, the second wording is "the person talks about losing interest in a planner, app or tracking system, leaving it unused, or abandoning it after a while"). Each wording finds some statements the other misses. 4. While looking at frames, flag anything private in the window (family members, documents, account numbers or amounts on a screen, messages) in privacy_flag. 5. Rejected windows go into rejected.csv with the reason; they are counted, not dropped silently. A window that cannot be settled from the frames stays a candidate and goes to the researcher. 6. An empty result or a short list does not prove participants never did it. Tell the user, and for spoken categories grep the transcripts (for example grep -n -i "gave up" TRANSCRIPT_FILE, where TRANSCRIPT_FILE is the entry's transcript) as a free second pass.
These wordings ran in our test; adapt the object, setting and statement to the user's categories and keep the shape:
| Field | Query | Mode | maximum_results |
|---|---|---|---|
| paper_list | the person holds up, points at or writes in a handwritten paper list, notebook or planner page they use to keep track of things | precise | 30 |
| sticky_note | a sticky note that the person is using as a reminder, stuck on a page, a screen or a wall and shown close to the camera | precise | 20 |
| calculator_device | the person picks up a calculator or a second phone or tablet and holds it up to the camera to work something out | precise | 20 |
| car_setting | the person is recording the entry while sitting inside a car | precise | 20 |
| gave_up | the person says they gave up on or stopped using an app, a planner or a system because it did not stick (plus the second wording above) | precise | 20 |
| complain_slow | the person complains that a step, an app or a routine takes too long, is tedious or is too much work to keep up with | precise | 20 |
Why this chain: the category query finds the entries and windows, the contact sheet and full frame turn each window into a checked moment and decide "in use" versus "in the background", and the transcript adds the participant's own words. The flip runs both ways: a participant who says "I just write it down" gets checked for the paper in the frame, and a participant shown with a list gets checked for what they said about it.
Worked example from our test run
In our test run we used 12 public videos, 25.0 minutes in total, cut from Creative Commons lifestyle vlogs (meal planning, budgeting, bullet journals, a car vlog) to stand in for diary entries of 100 to 150 seconds. These are edited YouTube videos with some overhead shots, not real diary entries, so vertical phone footage and poor light were only partly represented. Before searching we wrote down from contact sheets which entries showed each category, plus distractors: a budget typed on a lined paper style tablet note, colored squares on the wall behind a speaker, a handheld microphone. From the upload to all 12 entries ready took 8.5 minutes, mostly waiting in the queue.
The paper_list query returned 8 windows: 8 real and 0 false, and it missed 1 entry, where a journal flip through filled only the last seconds. The typed tablet budget was never returned. Windows were wide: the median was 17.5 seconds, and one covered 117 seconds of a 120 second entry, so the contact sheet, not the window, sets the row's time. The sticky_note query returned 4 windows: 2 real, 1 false (a handwritten notepad sheet the reason text called a sticky note) and 1 we could not settle from the frames (layered colored notes in a planner that could be tabbed pages); the wall squares were not returned. The calculator and car queries each returned 1 window, 1 real, but the corpus had only one entry for each. A control query for a whiteboard, which no entry shows, returned 0 results.
For gave_up, the first wording returned 2 windows: 2 real, 0 false, and 2 missed of the statements in the subtitles. The second wording, after 1 rewording on the same corpus, also returned 2 real and 0 false with 2 missed; it found one statement the first wording missed and one both found, so together the two wordings found 3 of the 4 statements in the subtitles. The complain_slow query returned 1 window, 1 real, the same moment as a gave_up hit; no entry with a transcript had a clear "this step takes too long" complaint, so its recall was not measured. Two entries had no transcript; for those we read the summary segments, which described the scratch pad workaround but cannot supply quotes.
The consent question, the comparison against a real plan, and the user's browser upload path were not exercised in our test run.
Done when every window of every category has a verdict in state.json and the user has seen the real, rejected and candidate counts per category.
Step 6: Assemble the board and confirm the sample
Goal: one table the researcher pastes into the synthesis board.
- Build one row per verified moment. Narrow start and end to the cells or transcript lines that show it, not the whole Vivu window:
category,participant_id,entry_file,start_mmss,end_mmss,quote,quote_source,scene_description,frame_path,privacy_flag,status,insight
paper_list,P03,P03_day2_entry1.mp4,00:10,00:35,,none,"Seated in a living area, holds a handwritten lined scratch pad list up to the camera while talking",frames/paper_list_P03_day2_entry1_13.png,,verified,
- Field mapping:
| Field | Source | If unavailable |
|---|---|---|
| category | categories.json | none |
| participant_id | the file name with the confirmed pattern | UNCERTAIN |
| entry_file | the original file name from entries.csv | none |
| start_mmss, end_mmss | the contact sheet cells or transcript lines inside the window | the Vivu window, marked UNCERTAIN |
| quote | the transcript lines, word for word | NOT VERIFIED |
| quote_source | the diary platform transcript or local speech to text | none |
| scene_description | Claude's description of the full frame (inferred) | none |
| frame_path | the full frame the row was checked on | blank for spoken rows |
| privacy_flag | what private content the frames show | blank |
| status | verified | candidate when it could not be checked |
| insight | left empty for the researcher | blank |
- Show two or three sample rows and the mapping and wait for a yes before writing the full board. Say which fields are inferred: scene_description is Claude's reading of the frame; if it is wrong, a background object can read as a workaround.
- Write evidence_board.csv and rejected.csv, and give the user the counts: rows per category and per participant, windows rejected per category and why, and the rows with a privacy flag.
In our test run the board (15 rows) and rejected.csv were built from the checked windows; the user's approval of the sample rows was not exercised in our test run.
Done when evidence_board.csv exists with one row per verified moment and the user has approved the sample rows.
Step 7: Create a Notion page (optional)
Goal: the board as a Notion page under the parent page the researcher chose.
- Only if the user asked for it in the inputs. Render the page from evidence_board.csv: one section per category, one line per row with participant, entry file, time and scene or quote. Use the entry file and time, never a Vivu result page link (it expires after four hours). Leave out rows with a privacy flag unless the user says the page stays inside the research team.
- Creating the page acts as the user. Name the parent page, show the full rendered page, and wait for an explicit yes. Record the page in state.json so a rerun never creates it twice.
Step 7 was not exercised in our test run: the page was rendered to a file and the Notion connector was never called.
Done when the approved page exists and is recorded in state.json, or the user chose evidence_board.csv only.
Compliance
- Index only entries whose participant consent covers uploading the videos to a third party cloud service for analysis. Ask before Step 2 and leave out every entry that is not covered. Do not index entries from participants under 18; diary studies with minors need guardian consent and a separate review.
- Diary entries show homes, families and screens. Create the Vivu project as private (the default is visible to the whole organization). The entries stay in the user's Vivu project until the user deletes them; the skill never calls vivu_delete_video or vivu_delete_project unless the user asks, and confirms first. If a participant withdraws, tell the user which videos to delete.
- Flag family members, documents, account details and private messages in privacy_flag, and keep flagged rows out of anything shared beyond the research team.
- No face, voice or identity recognition. Participant IDs come only from file names.
- The board records what entries show and say. It never rates or judges participants.
Known failure modes
| Symptom | Cause | Fix |
|---|---|---|
| (observed) the reason names an object that is not there; in our test run 1 of 4 sticky_note windows was a lined notepad sheet that the reason called "A handwritten sticky note with meal reminders and grocery notes is stuck onto a planner page" | the reason text describes a similar object with the query's words | judge every window from the contact sheet and a full frame, never from the reason |
| (observed) a window covers almost the whole entry; in our test run one paper_list window was 117 seconds | precise returns a range that contains the moment, sometimes the whole entry | narrow the row's time to the contact sheet cells that show the object |
| (observed) an entry that shows the workaround gets no window; in our test run the paper_list query missed 1 entry where the list appeared only in the last seconds | small objects that are on screen briefly are weak for visual search | for entries the researcher cares most about, make a contact sheet of the whole entry; an empty result does not prove the footage has no such moment |
| (observed) each spoken wording misses statements the other finds; in our test run each gave_up wording missed 2 | the same experience is said in many ways | run two wordings per spoken category and merge, then grep transcripts |
| (observed) a window cannot be settled from the frames; in our test run 1 sticky_note window was layered colored notes that could be tabbed pages | small objects at phone resolution look alike | keep it as a candidate in rejected.csv for the researcher, never as verified |
| an object lying in the background is reported as a workaround | visual search reads the scene coarsely; who uses what is easy to miss | write the query as an action (holds up, points at, writes in) and reject windows where nobody touches it |
| an entry has no transcript, so a spoken window cannot be quoted | many diary platforms export no transcript | keep the row as a candidate with quote NOT VERIFIED, read the summary segments, and ask the user to listen |
| the reason text quotes the participant with words they did not say | the reason is a paraphrase, not a transcript | copy quotes from the transcript only |
| a list stops at exactly maximum_results | the cap cut the list short | raise maximum_results and rerun that category |
| "has not granted vivu.write" | Vivu connected read only | the user reconnects Vivu with write access |
| upload page asks to sign in or shows an error | the one time upload link expires after 180 seconds | request a new link right before opening it |
| a file is rejected by the browser upload tool | Claude in Chrome takes at most 10 MB per upload call | the user adds that file in the Vivu web app; never split or recompress it |
| a result's file name does not match entries.csv | Vivu replaced spaces or punctuation in the name | compare names with those characters replaced by "_" |