Demos & Environments

A Journey replays the same way every time, so it can be shown as well as checked: live in a visible browser, as a recording, or as a narrated video and step-by-step guide. A demo that no longer replays fails, like a test.

Demo mode: watch or record a run

Every run is headless by default, check and CI included. Demo mode is opt-in:

# A visible Chromium, each browser operation slowed by 250 ms (override with --slow-mo <ms>)
jevitate explore --strategy adversarial --url http://localhost:3000/settings --fake-ai --headed

# Headless, recorded: the videos are listed in the result (videoPaths) and the summary (VIDEO lines)
jevitate explore --strategy adversarial --url http://localhost:3000/settings --fake-ai --record-video
jevitate journey run checkout --record-video --screenshots
FlagWhat it doesCommands
--headed (or JEVITATE_HEADED=1)A visible Chromium windowexplore (every strategy), journey run, journey demo, demo, demo approve, verify-fix, regression capture, regression run
--slow-mo <ms>Delays each browser operation; 250 by default with --headedthe same
--record-video [dir]A video of each browser context, headless too; listed as videoPathsexplore, journey run, verify-fix
--no-overlayWith --headed: hide the on-page overlay (step, intent, target highlight, outcome banner)explore
--screenshots [mode|dir]Masked screenshots plus an index.md contact sheet (below)explore, journey run, journey annotate, journey demo, verify-fix
--evidence-videoPer defect: a captioned repro clip and before/at screenshots (evidence)explore
  • The overlay is invisible to jevitate's own perception: it never changes what a run sees or decides.
  • --headed needs a display. On Linux without DISPLAY or WAYLAND_DISPLAY (a CI container, WSL2 without WSLg) it is refused before any browser launches (exit 64), with a pointer to --record-video.
  • Never headed: mission run (what MCP queue_exploration feeds), load run and source run. A check suite item opts in with its own headed, slowMo, recordVideo and overlay options.

Environments: --env

A Journey is environment-free: recorded and authored Journeys keep app-relative paths, and the site they were recorded on is only their default. Name the places your app runs in the committed .jevitate/environments.json (jevitate init writes an example once and never overwrites it):

{
  "local":   { "baseUrl": "http://localhost:3000" },
  "staging": {
    "baseUrl": "https://staging.example.com",
    "allow": ["https://auth.example.com"],
    "fixtures": "fixtures/staging.json",
    "hooks": { "before": "./scripts/seed-staging.sh" }
  },
  "prod": { "baseUrl": "https://app.example.com", "production": true }
}
jevitate journey run checkout --env staging
jevitate journey run checkout --base-url https://pr-123.preview.example.com   # an ad-hoc environment
jevitate journey annotate checkout --env staging --real
jevitate regression run cart-total --env local
jevitate load run checkout --env staging --authorized-origin https://staging.example.com
  • baseUrl is an origin. The Journey's recorded same-origin URLs move onto it, with their path, query and fragment.
  • The run's allowlist is the environment: baseUrl plus allow. A step on any other origin, or an unknown --env, is refused (exit 64) before any browser opens.
  • fixtures and hooks apply when --fixtures, --before and --after are not given. Hooks still need --allow-shell-hooks.
  • "production": true marks a live environment. demo refuses it.
  • --base-url alone is an ad-hoc environment; with --env it replaces that environment's baseUrl.
  • --env / --base-url work on journey run, journey annotate, journey demo, regression run and load run, and as env / baseUrl on a check Journey item.
  • Without either flag, a Journey runs on its recorded site, exactly as before.
No secrets in the file. A storageState, secret, password, token, cookie, credential or apiKey key anywhere in environments.json is refused. Sessions and secret fields per environment live in ~/.jevitate/targets.json, keyed by origin, and runs start from that session when you pass no --storage-state. A Journey's own vault secrets stay bound to the origin they were recorded on.
{
  "https://staging.example.com": {
    "storageState": "sessions/staging.json",
    "secretFields": ["label=Password=env:STAGING_PASSWORD"],
    "personas": { "admin": { "storageState": "sessions/staging-admin.json" } }
  }
}

Journey intent: draft it, then approve it

Optional, additive fields tell a Journey's why, not only its what: metadata.goal, persona, role, preconditions, successCriteria, parameters, and per-step objective and expectedResult. journey annotate drafts the missing ones by replaying the Journey and reading the redacted page before and after each step:

jevitate journey annotate checkout --env staging --real   # or --fake-ai for a deterministic smoke
# review or edit .jevitate/journeys/.drafts/checkout.annotations.json
jevitate journey annotate checkout --approve              # the human gate: shows the diff, then writes
  • Drafts go to a sidecar, never into the Journey. init keeps journeys/.drafts/ out of git.
  • --approve writes only the intent fields. It never promotes the Journey or changes an action or an assertion.
  • A draft is bound to the Journey's content hash: if the Journey changed, approval is refused (E_JOURNEY_ANNOTATIONS_STALE, exit 64).
  • Exit 0 drafted or applied · 2 the replay stopped early (a partial draft) · 64 bad input.

A narrated demo from a Journey: journey demo

jevitate journey demo checkout --env staging --video demos/checkout.webm --guide docs/checkout.md
jevitate journey demo checkout --headed --pace 2500        # present it live
  • The overlay shows the Journey's goal as a title card, then each step's objective as the caption (else its label), with the target highlighted and a --pace pause (default 1500 ms), then an outcome card. Annotate first so every step has an objective.
  • --video <file.webm> writes the recording and a .vtt beside it: one WebVTT cue per step. Convert to MP4 with ffmpeg if you need it.
  • --guide <file.md> writes a Markdown guide: goal, preconditions, and per step its objective, expected result and a screenshot (overlay hidden).
  • With neither flag, both go to a fresh folder in the logs directory. Headless by default, for CI.
  • The replay is a journey run (same params, session, environment, fixtures, site policy, fail-closed policy), so paid or destructive steps are refused as usual and a demo never self-heals.
ExitWhen
0Replayed, and every requested output was written
1Stale: the Journey no longer replays; nothing written
2The replay completed but an output could not be produced
64Bad arguments (--video not .webm, --guide not .md), an unknown Journey, --headed without a display

From one sentence: demo "<aspect>"

jevitate demo "Save your display name" --env staging \
  --success "textIncludes:testId=status|Saved" --real
# review the Journey, its annotations and the DRAFT video/guide, then:
jevitate demo approve demo-save-your-display-name
  1. Explore and author. A goal-directed exploration toward the aspect writes a Journey. --success is the independent check that proves it was shown: Jev drives, code decides.
  2. Clean path. Detours and dead ends are dropped, the same delta debugging as regression capture; a step goes only if the Journey still replays to its check without it.
  3. Annotate. Each step's objective and expected result are drafted, as journey annotate does.
  4. Draft demo. Video, .vtt and guide, watermarked DRAFT, to --out <dir> or the logs directory.

Nothing is written unless every stage succeeds, and nothing is promoted until demo approve <id>: it renders the final demo on the same environment, applies the annotations and promotes the Journey. After that it is an ordinary Journey: re-render it with journey demo --env <any>, or check it in CI with journey run. demo create <aspect> is the long form.

Never on production. demo runs only against a named environment (--env is required). One flagged "production": true is refused before anything runs (E_DEMO_PRODUCTION_ENV, exit 64), both when drafting and when approving. Paid, destructive and session-ending controls stay refused unless that origin's safety in ~/.jevitate/targets.json allows them.

Exit 0 drafted or approved · 1 the check was not reached or the path did not replay (nothing written or promoted) · 64 no --env / --success, a production environment, an existing id, or no draft to approve.

Screenshots and contact sheets

jevitate journey run checkout --screenshots              # one per distinct screen + index.md
jevitate journey run checkout --screenshots steps        # one per step
jevitate verify-fix --result <run>.result.json --fingerprint <fp> --screenshots screens:shots/

screens (the default) takes one screenshot per distinct screen, deduplicated by the page-state fingerprint coverage uses (a typed value is not a new screen); steps takes one per step. screens:<dir>, steps:<dir> or <dir> pick the folder. Each image is the viewport after the step acted, overlay hidden, and index.md lists each one with its step, route and what happened. The result lists screenshotPaths, screenshotIndex and, for a refused capture, screenshotsSkipped. A check item takes screenshots too.

Secrets are masked in the pixels

Every registered secret (--secret, secret fields, a Journey's secret parameters) shown on the page, in text, a field's value or an attribute, is painted over by a display-only layer; the page's own DOM is never changed. The mask is proven before and after every screenshot and at every step of a clip. Fail closed: a screenshot that cannot be proven is not written, and a clip whose mask failed at any step is deleted; the reason is recorded. Not covered: a secret drawn on a canvas, inside a closed shadow root, or under a top-layer modal (that last case is detected and refused). Captions, subtitles and guides are redacted as text too.

Full reference: docs/cli.md. Over MCP, the same commands are demo_journey, create_demo, approve_demo and annotate_journey (MCP & CLI).