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).

FieldMeaning
personaA 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.
strategygoal (default), coverage, exploratory, adversarial or usability.
goalRequired for goal and usability. A goal target may set the feature option instead.
tagsRun tags. JSON: an object. TSV: key=value entries separated by ;. The key target is reserved.
explore optionsJSON: 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.