---
name: release-notes-screenshot-sheet
description: "Turn sprint review recordings into a release notes sheet with Vivu: for each shipped ticket, find where an engineer demos it, grab a screenshot and their words, and flag tickets never shown."
---Release notes screenshots from sprint demo recordings
This skill takes the sprint review recordings that cover a release (meeting recordings where engineers take turns sharing their screen to demo finished work, exported as mp4 with a transcript) and the list of tickets in that release, and returns a release notes sheet: for each ticket, the recording and minutes where an engineer has the new screen up and says what changed, the page title read from a frame, a screenshot frame picked from inside the demo, the engineer's words from the transcript, and an empty column for the product manager or product marketer to write the release note. Tickets that were only talked about and never shown get a NO_SCREEN row, so nobody goes looking for a screenshot that does not exist. Claude cuts each recording down to the demo part using the meeting agenda, prices the indexing against the user's Vivu plan, indexes the cuts in a private Vivu project, runs one screen search per ticket, checks every hit against frames and the transcript, falls back to a speech search for tickets the screen search did not find, and writes release_sheet.csv.
The value is in what only the screen share shows. Ticket titles are short and written for engineers ("Bulk close stale tickets"), the button on screen says something else ("Close selected"), and the engineer says something else again ("clean up the old conversations nobody answered"), so a transcript search for the ticket title finds nothing, and what the new screen looks like exists only in the picture. The transcript has its own blind spot: an engineer who says "the dark theme slipped, I can't show it" names a feature that was never on screen. The screen alone is not enough either, because engineers click through pages on the way to their demo. So Vivu finds where each ticket's page stays on screen, a sheet of frames shows how long it stayed and what was clicked, and the transcript shows the engineer explaining the change; a row is verified only when all three agree.
When to use
Use when a product manager or product marketing manager says "pull screenshots for the release notes from the sprint review recordings", "for each ticket in this release, find where it was demoed", "which of these tickets were actually shown in the sprint demo", or "I need the engineer's own words on what changed for the changelog". For one question about one recording ("when did she show the export button?"), search Vivu directly. For clips of reps explaining modules to customers, use a sales demo library skill instead. This skill does not judge engineers or the quality of a demo, and it does not write the release notes; the product marketer writes them from the sheet.
Working principles
- Report measured numbers, not estimates. When a number is an estimate, say so.
- Vivu's results are candidates. A ticket gets a verified SHOWN row only after the frames show its page staying on screen with the change in view and the transcript shows the engineer explaining it. Rejected candidates are counted and kept in rejected.csv with the reason.
- Stop and tell the user when a required capability or file is missing (no transcript, no ffmpeg, no write access in Vivu). Do not guess around it.
- Ask the user before anything that is expensive to redo or that acts on their behalf: the ticket list, the searches and the cuts before upload, the indexing cost before upload, and the sample rows before the full sheet.
- Find moments; never judge people. The sheet has tickets and times, no ratings of engineers or demos.
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 recordings | cut the demo part, measure minutes, extract frames and screenshots | ffmpeg -version and ffprobe -version |
| The sprint review recordings as local mp4 files (downloaded from Zoom, Google Meet, Teams or Loom) | cuts, frames and screenshots come from the local files; Vivu has no export tool | ls REVIEWS_FOLDER |
| A transcript per recording with timestamps (the meeting tool's VTT or SRT export, or the output of a local speech to text tool) | the explanation check and the quotes come from it, never from Vivu's reason text | ls REVIEWS_FOLDER for one .vtt or .srt per recording; tell the user which have none |
| The meeting agenda or run of show (who demos which tickets, in what order) | shortlisting the recordings and finding where the demos start and end | ask the user to paste it |
| The release's ticket list: ticket ID, title, and which page of the product it changes | one search per ticket | ask the user to paste it |
| 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 |
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. Nothing is scheduled and nothing is written outside the working folder.
Inputs to collect
Ask for anything missing, most important first.
- The tickets in this release, five to eight, each with ID, title, the page of the product it changes, and in plain words what the new thing on that page looks like (for example HD-2141, "Bulk close stale tickets", Inbox page, checkboxes on the ticket list and a button that closes the selected ones). Required. Exact button labels are not needed; the ticket's own wording is fine.
- The folder with the recordings and transcripts (REVIEWS_FOLDER). Required.
- Which recordings to index. Default: the sprint reviews whose dates fall inside the release, usually one or two.
- Where the demos start and end in each recording. Default: Claude proposes it from the agenda and the transcript (from the first engineer's "let me share my screen" to the last demo, leaving out chit chat, standup and retro talk) and the user confirms.
- The Vivu project name. Default: "Release notes RELEASE", private, where RELEASE is the release name or version.
Files and state
Keep everything in one working folder next to the recordings:
release-notes/
tickets.json ticket ID, title, page, what it looks like, screen query, speech query
recordings.csv file, meeting date, CUT_START, CUT_END, minutes, transcript file
cut/ each recording cut to the demo part (Step 3), the files that get uploaded
results/ raw search results, one JSON file per ticket and search
frames/ sheets, full frames used to read titles, and the picked screenshots
release_sheet.csv one row per ticket and demo, verified or candidate, plus NO_SCREEN and NOT_FOUND rows
rejected.csv candidates that failed a check, with the reason
state.json uploaded files and video ids, searches run with job ids, every hit with its verdict, rows written
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, and checked hits keep their verdicts, so an interrupted run resumes where it stopped.
Step 1: Check the Vivu connector and the setup
Goal: every row of What you need is confirmed before anything is cut or 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 REVIEWS_FOLDER and note which recordings have a transcript file.
- Create the working folders; ffmpeg does not create an output folder:
mkdir -p release-notes/cut release-notes/results release-notes/frames
Run every later command from inside release-notes/ (cd release-notes), so cut/, frames/ and results/ resolve to the folders just created.
Done when vivu_get_account shows can_create_projects: true, ffmpeg and ffprobe print versions, and the working folders exist.
Step 2: Confirm consent, collect the tickets and write the searches
Goal: tickets.json with one screen query per ticket, approved by the user.
- Ask Compliance items 1 and 2 before anything else in this step. Leave out any recording that fails them.
- Write one screen query per ticket from its page and what the new thing looks like, using this shape:
| Field | Query template | Mode | maximum_results |
|---|---|---|---|
| TICKET_KEY | the presenter stays on the "PAGE_TITLE" page of the shared screen for a while and WHAT_THE_TICKET_DOES (WHAT_IS_VISIBLE), not a page that is only clicked past for a second or two on the way to another page | precise | 10 |
PAGE_TITLE is the page's title as the product shows it. WHAT_THE_TICKET_DOES is the ticket in a few plain words ("closes several stale tickets at once"). WHAT_IS_VISIBLE is two or three things on the page, including the new control described the way the ticket describes it ("a ticket list with checkboxes and a bulk close button"). Describe the screen, not the talk: engineers rarely say the ticket title out loud. The "stays ... not clicked past" clause matters, because engineers click through the same pages on the way to their own demo and a query that describes only the page returns those passes too. For a ticket with no visible change (a backend speedup), still write a screen query for the page it would show on; an empty result sends it to the speech search in Step 7. 3. Write one speech query per ticket and keep it for Step 7: "an engineer says out loud that WHAT_CHANGED", in plain words. 4. Show the tickets, the queries and the recordings to index, and wait for a yes.
All searches are precise because the sheet needs time ranges; a fast search only returns whole files, and the recordings are already chosen. maximum_results 10 is more than the demos one ticket gets in one or two reviews, and it is the ceiling on what comes back: a list that stops at exactly 10 means the cap was hit, so raise it and rerun that ticket.
In our test run the recordings were made for the test, so the consent questions were not exercised in our test run; the tickets and queries were written as above.
Done when the user has confirmed consent and approved tickets.json.
Step 3: Cut each recording to the demo part
Goal: one file per recording that holds only the demos, in cut/, so index minutes are spent only where a demo can be.
- From the agenda and the transcript, find where the demos start (the first engineer shares the screen) and end (the last demo, before retro talk or planning). Show CUT_START and CUT_END in seconds per recording and let the user adjust.
- Cut with re-encoding, so the cut starts exactly at CUT_START and every time Vivu returns maps back to the full recording by adding CUT_START (RECORDING_FILE is the original file in REVIEWS_FOLDER; CUT_FILE is its name without .mp4, so rows match back):
ffmpeg -v error -ss CUT_START -to CUT_END -i RECORDING_FILE -c:v libx264 -c:a aac cut/CUT_FILE.mp4
A stream copy (-c copy) is faster but starts at the nearest keyframe, a few seconds early, and then every time in the sheet is off by an unknown amount. Record CUT_START in recordings.csv.
In our test run both narrated recordings were cut this way, removing the chit chat at the start and the handover at the end, and the times in the sheet were mapped back by adding CUT_START.
Done when cut/ holds one file per recording and recordings.csv records each CUT_START.
Step 4: Price the indexing and get approval
Goal: the user sees the cost before anything is uploaded.
- Measure each cut file and sum the minutes. ffprobe takes one input file per call, so run it once per file:
ffprobe -v error -show_entries format=duration -of csv=p=0 cut/CUT_FILE.mp4
- Call vivu_get_usage for the plan and what remains this month.
- Estimate search credits: one precise screen search per ticket, plus one speech search for each ticket the screen search does not find, plus one rewording per ticket in case a 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 run | 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. As an estimate, two uncut sprint reviews of 40 minutes each would use almost half of Premium and do not fit Free, which is why Step 3 cuts to the demos. If the cut files still do not fit, offer these levers in order: tighten the cuts to the demos of tickets in this release only, index one review this month and one next month, and only then a larger plan. Never drop a recording the user asked for without saying so.
In our test run the account was an admin account whose vivu_get_usage shows no remaining allowance, so the comparison against a real plan and the approval were not exercised in our test run.
Done when the user has approved the minutes and the credit estimate.
Step 5: Upload and index
Goal: every cut file 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". Internal meetings show unreleased work and sometimes customer data; 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 in cut/, or attach them with a browser tool that can upload local files. Claude in Chrome accepts at most 10 MB per upload call, and a long screen share is usually larger; the user adds those in the Vivu web app. Never split or recompress a recording to fit.
- Poll vivu_list_videos about every 30 seconds until every file shows ready. Match each video back to recordings.csv by file name; Vivu turns spaces and punctuation in names into "_", so compare names with those characters replaced. Record the video IDs in state.json.
In our test run the files went up through a script, not through a user's browser or Claude in Chrome; that upload path was not exercised in our test run.
Done when vivu_list_videos shows every file in recordings.csv as ready.
Step 6: Search each ticket on the screen and check every hit
Goal: for each ticket, a list of checked demos with rejected candidates counted.
Run each ticket's screen query with vivu_search_videos (project_id, query, mode "precise", maximum_results 10). 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/TICKET_KEY_screen.json. Show the result page link in the live reply only; it expires after four hours, so it never goes into the sheet or any file.
Vivu returns a time range that contains the demo, not the exact seconds, and the reason text is a paraphrase that sometimes reports clicks that did not happen. Finding a page by its title and visible controls has held up in tests; telling a demo that stays on a page from a page clicked past is a weak capability, tested only on synthetic recordings with clean pages, so the sheet in check 1 decides, never the search alone. Check every hit in this order:
- Did the page stay, and did the change happen on it? Make a sheet of one frame every half second across the window (START is start_ms / 1000, DURATION is (end_ms - start_ms) / 1000; the 8x6 grid holds 24 seconds, so for a longer window raise the second number):
ffmpeg -v error -ss START -t DURATION -i cut/CUT_FILE.mp4 -vf "fps=2,scale=320:-2,tile=8x6" -frames:v 1 frames/TICKET_KEY_HIT_sheet.png
HIT is h plus the hit's number within the ticket (h1, h2, ...). Keep the quotes around the filter. Count the cells that show the ticket's page; each cell is half a second. Windows usually start and end on the neighboring pages, so the first and last cells often show something else. Reject the hit as PASS_THROUGH when the page is up for less than about 8 seconds (16 cells), and as NO_CHANGE_SHOWN when the page stays but the new control is never used or in view. Write the stay's first and last second into state.json. 2. Did the engineer explain it? Read the transcript lines between the stay's first and last second. The hit is verified only if the engineer says what changed or what it is for. When the talk is about something else, reject it as NO_EXPLANATION. When a recording has no transcript, keep the hit as a candidate: read vivu_get_video_summary with include_segments, start_ms and end_ms around the stay as a second signal only, set the quote to NOT VERIFIED, and tell the user to listen to that stretch. 3. Read the page title from a full frame in the middle of the explanation (SECONDS), not from the reason text and not from the window's first frame, which usually still shows the previous page:
ffmpeg -v error -ss SECONDS -i cut/CUT_FILE.mp4 -frames:v 1 -q:v 3 frames/TICKET_KEY_HIT_read.png
If the title is not the ticket's page, reject the hit. 4. Pick the screenshot. On the sheet, find the cell where the change is most visible (the confirmation message after the click, the toggle switched on, the preview panel filled in) and the cursor is off the thing being shown if possible, then extract that moment as a full frame with the same command, named frames/TICKET_KEY_HIT_pick.png. Look at it. If it shows a customer's name, real account data, a secret or key, or an unreleased item on a roadmap, keep the row but set flag SENSITIVE so the frame stays out of anything public. 5. Rejected hits stay in results/ and go into rejected.csv with the reason; they are counted, not dropped silently.
These wordings ran in our test, for four tickets of one help desk app; adapt the page, the ticket wording and what is visible to the user's product and keep the shape:
| Field | Query | Mode | maximum_results |
|---|---|---|---|
| bulk_close | the presenter stays on the "Inbox" page of the shared screen for a while and closes several stale tickets at once (a ticket list with checkboxes and a bulk close button), not a page that is only clicked past for a second or two on the way to another page | precise | 10 |
| sla_warning | the presenter stays on the "SLA policies" page of the shared screen for a while and shows the new warning before an SLA is breached (a list of SLA policies with a breach warning setting), not a page that is only clicked past for a second or two on the way to another page | precise | 10 |
| macro_preview | the presenter stays on the "Macros" page of the shared screen for a while and previews a macro's reply before applying it (a list of macros next to a preview of the reply text), not a page that is only clicked past for a second or two on the way to another page | precise | 10 |
| csv_export | the presenter stays on the "Reports" page of the shared screen for a while and exports a report as a CSV file (a chart with a date range and an export button), not a page that is only clicked past for a second or two on the way to another page | precise | 10 |
Why this chain: the screen query finds where the ticket's page is on screen, the half second sheet turns the window into a measured stay and rejects pages that were clicked past, the transcript flips from what was shown to what was said so a stay without an explanation never becomes a verified row, and a ticket the screen search cannot find goes to the speech search in Step 7 instead of being dropped.
Worked example from our test run
In our test run we used 3 synthetic recordings, 3.04 minutes in total after cutting, made for the test: a single help desk admin app whose pages share the same layout, navigation and colors, plus a camera grid where nobody shares a screen. In the 2 narrated recordings, synthetic engineer voices demoed tickets with real clicks while explaining what changed for users, never saying the page name, a control label or the ticket title; each engineer also clicked briefly through other pages and mentioned another ticket out loud on the camera grid without showing it. The last recording was a silent control with a demo of each of the 4 demoed tickets and the same kind of brief passes, and no voice at all. Before searching we wrote down every demo, pass and mention: 8 demos across the 4 demoed tickets. From the upload to all 3 recordings ready took 1.9 minutes.
The four screen queries, written in the tickets' own words rather than the labels on screen, returned 8 moments: 8 real, 0 false and 0 missed. That was the first wording on this corpus; the "stays ... not clicked past" shape came from an earlier test on a different synthetic corpus, where a page-only wording had returned every brief pass as well. Every brief pass through the same pages was left out. The silent recording returned 4 of its demos, so finding the demos came from the picture, not from the narration. Windows were 20 to 22 seconds per ticket (median), a little wider than the demo on both sides, so the sheet, not the window, gives the start and end. The screen queries for the two tickets that were only mentioned returned 0 moments; the speech searches then found both mentions, 2 real and 0 false, and those tickets got NO_SCREEN rows. The half second sheets showed every stay and click, and the full frames read the right page title every time. The transcripts were the test script with its measured timing, not a meeting tool export or speech to text output. In our test the ticket wording was close to what the screen showed; tickets whose wording is far from the screen, real screen shares with scrolling, pop ups, long pages, a second monitor or customer data, and a page idling on screen while the engineer talks about something else were not exercised in our test run.
Done when every hit of every ticket has a verdict in state.json and the user has seen the real and rejected counts per ticket.
Step 7: Search speech for tickets with no screen hit
Goal: every ticket with no verified SHOWN row is either found in the talk (NO_SCREEN) or marked NOT_FOUND.
- For each ticket whose screen search returned nothing, or whose hits were all rejected, run its speech query from Step 2 with vivu_search_videos (project_id, query, mode "precise", maximum_results 10) and poll as in Step 6. Save it as results/TICKET_KEY_speech.json.
- Check each hit against the transcript lines inside the window: the engineer must name this change. Read vivu_get_video_summary with include_segments, start_ms and end_ms around the window as a second signal. The quote comes from the transcript only.
- A confirmed mention becomes a NO_SCREEN row with the quote and time and no screenshot. Look at one frame in the window to confirm nothing relevant is on screen; if the feature is on screen after all, go back to Step 6 check 1 with that window.
- When the speech search also finds nothing, write a NOT_FOUND row and tell the user. An empty result does not prove the ticket was never demoed; the page may look different from the query, or the demo may be in a recording that was not indexed.
These speech wordings ran in our test, after the screen searches for the same tickets came back empty:
| Field | Query | Mode | maximum_results |
|---|---|---|---|
| search_speed_said | an engineer says out loud that searching for tickets now returns results faster after backend work, with nothing new to show on screen | precise | 10 |
| portal_dark_said | an engineer says out loud that the dark theme for the customer portal is not ready to show in this review | precise | 10 |
In our test run both tickets that were only mentioned came through this step as NO_SCREEN rows; NOT_FOUND did not occur, so that branch was not exercised in our test run.
Done when every ticket has at least one row in state.json: SHOWN, NO_SCREEN or NOT_FOUND.
Step 8: Assemble release_sheet.csv and confirm the sample
Goal: one table the product marketer can sort by ticket and write release notes from.
- Build one row per verified or candidate demo and one row per NO_SCREEN or NOT_FOUND ticket. Times are the stay's first and last second from the sheet plus CUT_START, so they point into the full recording:
ticket,ticket_title,recording_file,start_mmss,end_mmss,page_title_read_from_frame,screenshot_path,what_changed_quote,quote_source,screen_status,flag,status,release_notes_text
HD-2141,Bulk close stale tickets,RECORDING_FILE,00:08,00:28,Inbox,frames/bulk_close_h1_pick.png,"Now you tick the ones you want, and they are gone in one go.",meeting transcript,SHOWN,,verified,
HD-2193,Faster ticket search,RECORDING_FILE,00:51,01:00,,,"The search backend work landed, so looking up a ticket should come back noticeably quicker.",meeting transcript,NO_SCREEN,,verified,
- Field mapping:
| Field | Source | If unavailable |
|---|---|---|
| ticket, ticket_title | tickets.json | none |
| recording_file | the original file name from recordings.csv | none |
| start_mmss, end_mmss | first and last cell of the stay on the sheet (or the transcript lines, for NO_SCREEN), plus CUT_START | the Vivu window plus CUT_START, marked UNCERTAIN |
| page_title_read_from_frame | read from the full frame | UNCERTAIN |
| screenshot_path | the picked full frame | blank for NO_SCREEN and NOT_FOUND |
| what_changed_quote | the transcript lines inside the stay, word for word, shortened to one or two sentences | NOT VERIFIED |
| quote_source | meeting transcript or local speech to text | none |
| screen_status | SHOWN, NO_SCREEN or NOT_FOUND | none |
| flag | SENSITIVE when the frame shows customer data, a secret or an unreleased roadmap item | blank |
| status | verified | candidate when the transcript is missing |
| release_notes_text | left empty for the product marketer | blank |
- Show two or three sample rows and the mapping and wait for a yes before writing the full table. Say which parts are inferred: whether a sentence "explains the change" is Claude's reading of the transcript, and the picked screenshot is Claude's choice of the clearest frame; if either is wrong, the product marketer sees it when writing the note and can pick another frame from the sheet.
- Write release_sheet.csv and rejected.csv, and give the user the counts: rows per ticket, SHOWN, NO_SCREEN and NOT_FOUND tickets, candidates rejected per ticket and why. When a ticket has no SHOWN row, say so plainly; do not fill it with a pass through its page.
In our test run the sheet and rejected.csv were built from the checked hits: the silent recording's rows stayed candidates with the quote NOT VERIFIED, because it had no transcript. The user's approval of the sample rows was not exercised in our test run.
Done when release_sheet.csv exists with a row for every ticket and the user has approved the sample rows.
Compliance
- Use only internal meetings recorded under the company's recording policy, where the people in the meeting knew it was recorded. Ask before Step 2 and leave out any recording that does not meet this.
- Sprint reviews show unreleased work and sometimes customer data. Ask before Step 5 whether the company allows uploading these recordings to a third party service, and create the Vivu project as private. The recordings 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.
- Screenshots that show customer names or data, secrets or keys, or an unreleased roadmap item are flagged SENSITIVE and stay out of public release notes. The product marketer looks at every screenshot before it is published.
- The sheet finds moments. It never rates engineers or demos.
- No face, voice or identity recognition. Who presented comes from the agenda, not from Vivu. Vivu only finds when a page is on screen or a topic is spoken; screenshots are extracted by the user's own ffmpeg from their own files.
Known failure modes
| Symptom | Cause | Fix |
|---|---|---|
| (observed) the first and last frames of a window show the pages clicked through before and after the demo | windows start and end a little outside the stay | take start and end from the sheet, and read the title from a frame in the middle of the explanation |
| (observed) the reason quotes a button label the query never used, such as "Close selected" | the reason reads the screen and paraphrases it | read labels and titles from the full frame; never copy them from the reason |
| (observed) the reason says a step happened that the frames do not show: "applies the selected macro" while the cursor only hovers the button | the reason is a paraphrase, not a reading of each frame | describe what happened from the sheet, not from the reason |
| (observed) the screen search for a ticket that was only talked about returns nothing | nothing on screen matches; the change was mentioned, not shown | run the speech search in Step 7 and write a NO_SCREEN row when the transcript confirms the mention |
| (observed) rows from a recording without a transcript cannot be verified | the explanation check needs the transcript | keep them as candidates with the quote NOT VERIFIED and tell the user to listen to that stretch |
| (observed) ffprobe stops with "provided as input filename, but" | ffprobe takes one input file per call | run ffprobe once per file |
| pages clicked past on the way to a demo come back as hits | a query that describes only the page finds every appearance of it, however brief | keep the "stays ... not clicked past" clause, and reject any hit whose sheet shows the page only briefly |
| a ticket comes back empty although it was demoed | an empty result does not prove the ticket was not shown; the ticket wording may be far from what the screen shows | check the page title and visible controls against a frame of that page, reword once with what is really on screen, then run the speech search |
| a list stops at exactly maximum_results | the cap cut the list short | raise maximum_results and rerun that ticket |
| sheet times point a minute early or late in the full recording | times were taken from the cut file without CUT_START, or the cut used -c copy and started at a keyframe | cut with re-encoding as in Step 3 and add CUT_START to every time |
| "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 recordings.csv | Vivu replaced spaces or punctuation in the name | compare names with those characters replaced by "_" |