Sweeps & Personas
A release check usually means dozens of missions: one per feature or route, often once per
persona. jevitate sweep runs them from one targets file with bounded concurrency,
resumes after an interruption, and writes one aggregated sweep.result.json.
jevitate sweep --targets release-targets.tsv --base-url https://staging.example.test \
--concurrency 3 --out .jevitate/sweeps/0.8.0 --tag release=0.8.0 --real
# after an interruption (or a fixed environment): finished targets are not re-run
jevitate sweep --targets release-targets.tsv --base-url https://staging.example.test \
--concurrency 3 --out .jevitate/sweeps/0.8.0 --tag release=0.8.0 --real --resume
Each target runs as the same jevitate explore command you would type yourself: the same
validation, gateways, invariants, checks and results. Runs go through the shared browser pool and
the machine-wide browser cap (--max-browsers), so --concurrency never exceeds
what the machine admits. Over MCP the tool is run_sweep.
The targets file
A .tsv file (a header row, then one target per line) or a .json file. Every
target has an id (1-64 of [A-Za-z0-9_.-], unique; it names the target's
directory and is the run's target=<id> tag) and either a url or a
route (a path starting with /, resolved against --base-url, else
--env's base URL, else the file's baseUrl, else JEVITATE_BASE_URL).
| Field | Meaning |
|---|---|
persona | A Playwright storage state for the run: a path (named after the file), name=<path>, or in JSON {"name", "storageState"}. Relative paths resolve against the targets file. |
strategy | goal (default), coverage, exploratory, adversarial or usability. |
goal | Required for goal and usability. A goal target may set the feature option instead. |
tags | Run tags. JSON: an object. TSV: key=value entries separated by ;. The key target is reserved. |
| explore options | JSON: an options object. TSV: one extra column per option. |
A JSON file is an array of targets, or an object with targets and optional
baseUrl and defaults (a strategy, goal,
persona, tags and options each target inherits unless it sets
its own; tags and options are merged, the target's winning).
{
"baseUrl": "https://staging.example.test",
"defaults": {
"strategy": "adversarial",
"tags": { "release": "0.8.0" },
"options": { "maxActions": 30, "deny": ["Delete account"] }
},
"targets": [
{ "id": "billing-admin", "route": "/billing", "persona": "sessions/admin.json", "tags": { "feature": "billing" } },
{ "id": "billing-viewer", "route": "/billing", "persona": "viewer=sessions/viewer.json", "tags": { "feature": "billing" } },
{ "id": "checkout", "route": "/checkout", "strategy": "goal", "goal": "buy the cheapest item", "options": { "success": ["urlIncludes:/order/"] } }
]
}
The same shape as TSV (tab-separated columns; empty cells are unset; lines starting with # are comments):
id route persona strategy goal tags maxActions deny
billing-admin /billing sessions/admin.json adversarial feature=billing;release=0.8.0 30 ["Delete account"]
checkout /checkout goal buy the cheapest item feature=checkout
Options a target may set are the value-typed arguments MCP
run_exploration takes, by their camelCase names (maxActions, deny,
paid, allowControl, feature, success,
minActions, minDistinctStates, authCheck, …). Options that name
files, read logs, run commands or bind secrets are not per-target options: give them once on the
sweep command line (--invariants, --log-source, --deny,
--paid, --allow-control, --allow-destructive,
--record-video, --screenshots, --jev-provider, …) and they are
forwarded to every run.
Validation. The whole file is checked before anything runs; every problem, with its
line or target, is listed in one refusal (E_SWEEP_SPEC, exit 64). A persona storage state must
be inside the targets file's directory, the project or ~/.jevitate/, and never inside a
repository's .jevitate/, the same rule an MCP storageState argument follows.
Browser flags, fixtures and exit (0.13.0)
- Browser launch flags.
sweep takes --browser-arg, --browser-executable, --browser-channel, --extension, --max-browsers, --max-browser-memory and --ignore-host-load and forwards them to every run. A per-target browserArg is refused with a pointer to the flag. These flags are CLI only; MCP run_sweep does not take them. - Upload fixtures. A target may carry its own
options.fixture (JSON, defaults.options or a TSV column), and sweep --fixture <file> sets one for the whole sweep. A target's own fixture wins. Both are confined like every upload fixture (inside the project, sensitive files refused) and checked when the spec loads, so a bad path fails the whole sweep up front (E_SWEEP_SPEC / E_SWEEP_ARGS). MCP run_sweep takes fixture. - A run ends after its result. A
cmd: or docker: log source's pipes are destroyed when it closes, so a helper that left the process group (for example ssh's ControlPersist master) no longer keeps sweep and other runs alive after they wrote a complete result.
Concurrency, resume and stopping early
--concurrency N (1-16, default 1) runs up to N targets at once, in file order. Concurrent runs share the app under test: give targets that write state --concurrency 1, or point them at separate accounts. --out <dir> is the sweep directory: <id>/ per target and sweep.result.json, rewritten after every finished run. --resume (needs --out) skips every target whose <id>/run.envelope.json holds a finished run and reads it back into the aggregate. A run that could not start is run again. --stop-on-env-failure K stops starting runs when the first K runs of this invocation all failed for an environment or setup reason (an expired session, an unreachable target, a starved host, a configuration or resource-limit failure, a crash). Runs in flight finish, the rest are skipped, the sweep is inconclusive (exit 2), and the message says to fix the environment and re-run with --resume. host-starved (0.9.0): a target that ends inconclusive with failure.kind: "host-starved" is retried once after a bounded wait for load to drop. --no-host-starved-retry records the first result instead. summary.retried counts retries, and summary.notStarted counts goal targets that executed zero actions (goalOutcome: not-started). Each target is stamped with target.id. - A killed sweep (SIGINT/SIGTERM) writes its partial
sweep.result.json (complete: false) before it exits 130/143.
The result holds each target's outcome and depth, defects deduped by fingerprint across targets (one
finding with N sightings) and environment causes grouped. Its fields are in the
Report Schema.
Run tags
--tag key=value (repeatable) is taken by explore, journey run,
check, load run, verify-fix, regression run,
demo, mission run, source run, campaign run and
sweep; MCP run tools take a tags object. A tag says which feature, journey or
release a run exercised, so a dashboard can attribute the run without guessing from its output path.
jevitate explore --url https://app.example.test/checkout --strategy adversarial \
--tag feature=checkout --tag release=0.8.0
jevitate report --tag feature=checkout --tag release=0.8.0 # only runs carrying BOTH tags
jevitate diff before after --tag feature=checkout # both sides narrowed to the tag
- A key is 1-64 of
[A-Za-z0-9_.-]; a value is 1-256 characters with no control characters; at most 32 tags. A bad tag is refused (E_TAG_ARGS, exit 64) before anything runs. - Tags are stored in
result.tags, the --json envelope, run.envelope.json and the run index. Results also record target.startUrl, target.strategy and target.persona. report --tag and diff --tag filter with AND semantics. A tag no run carries is refused (exit 64), never an empty report. - Tags are never redacted. Never put a secret in one.
Persona sessions: jevitate login
Each persona needs a Playwright storage state. jevitate login makes one from credentials
held in environment variables.
export ADMIN_USER=admin@example.com ADMIN_PASSWORD=... # never on the command line
jevitate login --persona admin --url http://localhost:3000/login \
--user-env ADMIN_USER --password-env ADMIN_PASSWORD \
--success urlIncludes:/dashboard --save ~/.jevitate/states/admin.json
- Only the variable names are given; a value pasted in their place is refused. jevitate's own keys are never typed into a page.
- Fields are found by label,
autocomplete, type and name (--user-field / --password-field take a label or CSS selector; --submit a button name). A two-step form works too. Success is urlIncludes:<text>, selector:<css> or text:<text>; by default the page must leave the sign-in form. A form that shows an error after submitting fails at once (E_LOGIN_FAILED, exit 2). - Credentials are typed only into a page on an authorized origin: the sign-in page's own, or
--allow (for example an SSO provider). jevitate login --api <url> (0.9.0) signs in through an HTTP endpoint instead of a form: it POSTs the credentials from the environment as JSON (--api-user-key / --api-password-key, default username / password) to an authorized origin, keeps the response's cookies and/or writes a token from the body (--token-path) into the app origin's localStorage (--storage-key), verifies the session (--verify-url, --auth-check), and only then saves it. Redirects to other origins are refused, and --storage session is refused. Also login.api in personas.json, which automatic re-login uses. - The login session records no trace, video, HAR or screenshot, and no result, error or log line carries a credential.
- The state is written atomically with mode
0600. A path inside a repo's .jevitate/ is refused. jevitate login is CLI-only: it reads the operator's credential environment variables, and there is no MCP tool for it.
The pre-flight auth check and refresh
Before a run that starts from a session (--storage-state, --persona, queued
missions), explore loads the state, opens --url, and ends the run at once if it
landed on a sign-in page: inconclusive (exit 2) with
failure: {kind: "auth-expired", message, persona}, plus persona,
startUrl and landedUrl. The login page is never explored. --auth-check
sets the rule:
--auth-checkThe session counts as expired when auto (default)the start URL lands on a login-like path (/login, /signin, /sign-in, /auth, /sso, …) or shows a visible password field. Not checked when --url is itself such a route urlExcludes:<text>the landed URL contains <text> selector:<css>the signed-in marker is not visible offnever checked
The check is one extra page load of the start URL in its own unrecorded context. In a persona matrix
every run is checked; an expired persona gets an auth-expired run in its cell and the other
personas still run. A persona can carry login parameters (environment variable names only) in
its personas file entry or .jevitate/personas.json: when the check finds its session expired,
or its state file missing, jevitate signs in once, saves the state over the old one and checks again;
if that fails the run ends auth-expired with the sign-in failure in the reason. Over MCP,
run_exploration takes authCheck, and a refresh uses only login parameters an
operator's personas file declares. Full detail:
docs/multi-run.md and
docs/sweeps.md.