record-user-journey
Use when you need a recording or visual proof of a real user journey through a running app — a demo video for a PR, ticket or stakeholder, evidence that a fix…
Skill by LRNZ09
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/lrnz09-record-user-journey
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/record-user-journey
What's inside
- Skill name
record-user-journey- Description
- Use when you need a recording or visual proof of a real user journey through a running app — a demo video for a PR, ticket or stakeholder, evidence that a fix behaves correctly end to end, a walkthrough captured at a fixed device viewport (TV, tablet, kiosk), or the same journey re-recorded later after code changed. Also use when a recording came out cropped, juddering, or showing something that did not happen.
- License
- MIT
- Instructions
- 219 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (8)
- SKILL.md
- driver.mjs
- instrument.js
- standby-teardown.example.json · journeys/
- overlay.js
- reference.md
- build-stack-filter.mjs · scripts/
- side-by-side.sh · scripts/
SKILL.md (first lines)
# Record a User Journey
Drive a running app over the Chrome DevTools Protocol, capture the journey as video, and
annotate it with what actually happened. The journey is data; the harness in this directory
executes it.
**Core principle: the recording is evidence, so it has to be honest.** A frame that shows a
held video still while the screen was off, a caption that claims success the run did not
achieve, a viewport that is really a cropped window — each turns a demo into a false claim.
Every rule below exists to keep the recording faithful.
## Prerequisites
1. The app running locally, and a Chrome you control:
```sh
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 --user-data-dir=/tmp/journey-profile \
--hide-crash-restore-bubble
```
**Do not pass `--disable-backgrounding-occluded-windows` or
`--disable-renderer-backgrounding`** — they stop the page ever being backgrounded, so any
journey that hides the app silently tests nothing. Automation-launched browsers
(including MCP-driven ones) commonly set them; launch your own instead.
If you copy a real profile to inherit a login, delete `SingletonLock`, `SingletonCookie`
and `SingletonSocket` from the copy first or Chrome hands off to the running instance and
never opens the port.
2. Sign in manually once. The profile keeps the session.
3. **Confirm the server is serving the code you intend to demonstrate.** Check the branch,
and check the code is reachable — an effect nobody subscribes to is dead code. Restart the
dev server after switching branches. Skipping this produces recordings of the wrong build
that look exactly like recordings of the right one.
4. **Start the dev server so it does not inherit your shell's file descriptors**, and load
the route you are about to record before recording it. A server backgrounded from an
agent's own shell (`nohup … &`, even with `< /dev/null`) keeps serving, but a plugin that
spawns a child process to compile — vanilla-extract does, per CSS module, lazily on first
request — fails with `spawn EBADF` once the parent's descriptors go away. The page then
returns 200 with an empty body, so the run aborts on a missing selector and looks like a
bad journey rather than a broken server. Use the harness's own detached-process mechanism,
check the server log for `EBADF`, and confirm the route's key element renders first.
## Running it
```sh
node driver.mjs journeys/my-journey.json --out ./recording
```
Outputs `recording/<name>.mp4`, the frames and concat list, and `journey.json` with every
fact the run collected plus a list of held frames. A `verifyViewport` step also writes
`verify-viewport.jpg`. `--no-encode` skips ffmpeg. The `frames/` subdirectory of `--out` is
cleared on each run; nothing else in it is touched.
## Before/after comparisons
Recording the same journey against two builds is the common case, and the pair only reads as
evidence if the presentation is identical every time. Run the same journey file twice, into
separate `--out` directories, then compose:
```sh
scripts/side-by-side.sh <before.mp4> <after.mp4> <out-dir> <TICKET> [before-label] [after-label]
```
**The standard, so a comparison looks the same across tickets:**
- **Filename** `<TICKET>-before-after-side-by-side.mp4`, with the single-build clips as
`<TICKET>-before.mp4` / `<TICKET>-after.mp4` and stills as `<TICKET>-still-<what>.png`.
Recognisable from the filename alone, and successive tickets sort together.
- **Labels are floating badges in each pane's top-left corner**, over the video: pastel red
`#F2A6A6` for before, pastel green `#A8DCB0` for after, so the colour says bad/good before
a word is read. Ink is a deep tint of the same hue (`#5A1111`, `#14431F`), never white —
white on either pastel measures under 2:1 and is unreadable, while these land at 7.1:1 and
7.3:1. 50px tall, inset 17x20 in a 1280-wide pane. Not a header bar above the panes — a bar
steals height from both and pushes the app's own top-of-screen UI (a search field, the
caption) away from the frame edge it was designed against. The badge sits in a corner apps
leave empty.
- **Geometry** two 1280x720 panes plus a 4px separator, so 2564x720; 30fps CFR, yuv420p.
- **A markdown summary next to the video**, `<TICKET>-before-after.md`: what was recorded in
two lines, then a table of every fact field whose value differs between the runs. The
script generates it from the two facts files, and omits rows that are identical — a table
where most rows say "unchanged" buries the two or three that carry the result.
- **Keep implementation detail out of that table, and make every row that stays readable on
its own.** A summary is read by people who were not in the investigation, and a number they
cannot interpret is worse than no number: it invites a wrong conclusion. Declare the noisy
fields in the journey as `summaryExclude` (leaf names, or `fact.field` for one specific
row); the script drops them and footnotes how many it withheld, so nothing is hidden
silently. Two kinds belong there:
- **Technical** — internal geometry with no user-visible meaning (a virtualizer's row
height in px, its loaded-row count, a JS heap figure that excludes the resource the fix
is actually about).
- **Misleading** — anything where a bigger number is not plainly better or worse. A raw
count of "tiles in view" varies with row height, overscan and how far a list has
paginated, so the same value can move in either direction for reasons unrelated to the
change. Prefer a self-describing form: `"6 of 6 recording tiles"` says what a bare `6`
cannot, and stays true whatever the window happens to contain.
- **Alignment** a journey with fixed holds runs to the same length twice, so any difference
is start-up latency at the head. The script trims the head of the longer clip so the two
align at the *tail*, where the summary panel is, and says so when it does. Drift then
lands in the idle opening rather than across a caption, where one pane would appear a step
ahead of the other.
The script needs ImageMagick: most ffmpeg builds ship without libfreetype, so `drawtext` is
unavailable and the badges have to be rendered as PNGs and overlaid.
**Both runs must see the same fixture data.** Two runs against drifting fixtures produce a
diff of the fixtures, not of the builds. Confirm the data is stable before the first run
rather than between them: a seeding job still landing rows during run one showed a third of
the continue-watching bars missing, which read exactly like the branch having dropped them.
**Keep the journey as short as the evidence allows.** Every extra step is a step that can
diverge between two runs, and a comparison is only as trustworthy as its shortest path to
the thing being shown.
- **Do not drive UI to reach a state the default view already shows.** A leg that switches a
filter or opens a menu to surface some item type is wasted when the unfiltered view already
contains it — and worse, a convergence target that is already satisfied lets the step skip
itself while the caption still claims the switch happened.
- **Scroll two or three rows, not to the end of the list.** Enough to force the next page and
show what stays mounted; more just adds runtime and lets the two runs drift apart. Reaching
the end of a list also changes what the virtualizer keeps, which muddies exactly the
measurement a scroll leg is there to take.From LRNZ09/skills, MIT.
More from LRNZ09/skills
- 0.539score
codename
LRNZ09
Use when a repository, project, service, or workstream needs a name and a descriptive one would be dull — 'what should I call this repo', 'give me a…
SkillAFreshness: AClaude Code +9 - 0.504score
diagnose
LRNZ09
Disciplined diagnosis loop for hard bugs and performance regressions.
SkillBFreshness: BClaude Code +9 - 0.499score
council
LRNZ09
Use when the user wants a decision, idea, or piece of work pressure-tested from multiple independent angles before they commit to it — not for questions with…
SkillBFreshness: BClaude Code +9 - 0.499score
grill
LRNZ09
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree one at a time with a…
SkillBFreshness: BClaude Code +9 - 0.499score
improve-codebase-architecture
LRNZ09
Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/.
SkillBFreshness: BClaude Code +9 - 0.499score
caveman
LRNZ09
Ultra-compressed communication mode.
SkillBFreshness: BClaude Code +9 - 0.499score
tdd
LRNZ09
Test-driven development with red-green-refactor loop.
SkillBFreshness: BClaude Code +9 - 0.488score
handoff
LRNZ09
Compact the current conversation into a handoff document for another agent to pick up.
SkillBFreshness: BClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Oct 13, 2026 (16 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/LRNZ09/skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from LRNZ09
- 0.539score
codename
LRNZ09
Use when a repository, project, service, or workstream needs a name and a descriptive one would be dull — 'what should I call this repo', 'give me a…
SkillAFreshness: AClaude Code +9 - 0.504score
diagnose
LRNZ09
Disciplined diagnosis loop for hard bugs and performance regressions.
SkillBFreshness: BClaude Code +9 - 0.499score
tdd
LRNZ09
Test-driven development with red-green-refactor loop.
SkillBFreshness: BClaude Code +9 - 0.499score
caveman
LRNZ09
Ultra-compressed communication mode.
SkillBFreshness: BClaude Code +9 - 0.499score
council
LRNZ09
Use when the user wants a decision, idea, or piece of work pressure-tested from multiple independent angles before they commit to it — not for questions with…
SkillBFreshness: BClaude Code +9 - 0.499score
grill
LRNZ09
Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree one at a time with a…
SkillBFreshness: BClaude Code +9
Cite this page
record-user-journey skill. Agent Mart. https://agentmart.dev/skills/lrnz09-record-user-journey (last updated 2026-10-24)