Skip to content

Require whirl run, add -O overrides, block-hosts, and settings in reports - #47

Merged
brynary merged 7 commits into
simpler-syntaxfrom
cli-settings
Oct 5, 2026
Merged

brynary merged 7 commits into
simpler-syntaxfrom
cli-settings

Conversation

@brynary

@brynary brynary commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #46.

This is phase 2 of the Whirl–BrowserSim delivery sequence: the CLI and settings rules. It does not integrate BrowserSim. It gives ordinary runs the agreed command line, and it gives later commands one shared way to resolve options.

This changes existing scripts. Whirl has no users to migrate yet, so there are no aliases except --artifacts.

Explicit run and renamed flags

Before After
whirl checkout.whirl whirl run checkout.whirl
whirl --artifacts DIR ... whirl run --out DIR ...
whirl --storage app.json ... whirl run --load-state app.json ...
whirl --save-storage app.json ... whirl run --save-state app.json ...
whirl --base URL --step-timeout 15s ... whirl run -O base=URL -O step-timeout=15s ...
  • whirl checkout.whirl and whirl --headed checkout.whirl fail with a hint to use whirl run. whirl alone prints help and exits 4.
  • --out defaults to whirl-artifacts/, as before. --artifacts still works and prints a deprecation warning. Supplying both is an error.
  • --storage and --save-storage fail with a message that names the new flag. The state file format does not change; SPEC section 13 documents it.
  • --load-state cannot be combined with a file's storage or setup option. The old --storage silently replaced the file's storage option.

-O key=value

whirl run and whirl check take repeatable -O arguments that set an [Options] key for every file, setup flows included:

whirl run checkout.whirl -O browser=firefox -O step-timeout=15s
whirl run flows/ -O block-hosts=analytics.example.com -O block-hosts=*.analytics.example.com
whirl run flows/ -O allow-hosts=
whirl check checkout.whirl -O model=anthropic/claude-sonnet-5
  • Settings resolve from the built-in default, then the file, then the command line.
  • Each value is one literal item, with the file option's validation. A snapshot-mask value is the exception: it is a locator, written as in a file.
  • The last value wins for a single-value key. For a list key, the -O items replace the file's list, and an empty value clears it.
  • An unknown key, an invalid value, and -O setup= are usage errors, found before any browser starts.
  • --browser is the one flag left that sets an option. It goes through the same resolver, and --browser together with -O browser= is an error, even when the values agree. --base, --step-timeout, --entry-timeout, and --user-agent are removed; each fails with a message that names its -O form.
  • whirl check lints with the overrides in place, and still launches no browser. A lint about a -O value has no line.

One module, lang::cli_options, owns this, so later commands can reuse it. It puts the command line's option lines in place of the file's, marked with their source.

block-hosts, browsersim-origin, and settings in reports

  • [Options] block-hosts blocks the hosts its globs match, with the glob rules of allow-hosts. A block wins over an allowance, including the base host's implicit allowance. It applies to page requests, redirect hops, WebSockets, and HTTP entries.
  • Each blocked host now shows its rule, as in blocked host: localhost (not in allow-hosts). The JSON report adds blockedHostRules; blockedHosts is unchanged.
  • browsersim-origin: build | recorded is checked but not used by ordinary runs. When a file or -O sets it, the console and reports list it as inactive. Other browsersim- keys stay unknown.
  • The JSON and HTML reports list each file's settings: the value it ran with, masked like other output, and whether it came from the default, the file, or the command line.
  • The shim moves to protocol 9, because endFlow now returns a rule with each blocked host. Installed bundles need whirl install.

Recording contract

The accepted ADR recording-settings-and-bundles fixes what later recording work must follow. whirl record is the only recording command. --engine and --browser stay separate. A *.sim.json recording stores every effective setting, and replay uses only those. Each written flow records to a sibling bundle named after its stem. It adds no commands.

Commits

  1. Run files with whirl run, and rename the output and state flags.
  2. Set options from the command line with -O key=value.
  3. Add block-hosts, an inactive browsersim-origin, and settings in reports.
  4. Draft the recording settings and bundle contract as an ADR, then accept it.
  5. Take each -O value as one literal item.
  6. Remove --base, --step-timeout, --entry-timeout, and --user-agent.

Validation

  • mise run check passes after each commit.
  • CLI cases cover the removed forms and each -O error.
  • Browser tests use the local test site, whose page fetches the same server under a second host name. They cover:
    • -O across two flows: the last value wins, and list items replace or clear the file's list.
    • block-hosts alone, block-hosts with allow-hosts, and blocking the base host, for page requests and an HTTP entry.
    • An inactive browsersim-origin, and effective settings that keep an env.* user agent out of the console, JSON, and HTML output.
    • The default output directory, --out, and the --artifacts alias.
  • CI below.

🤖 Generated with Claude Code

brynary and others added 6 commits September 30, 2026 06:47
Files now run only through `whirl run`. `whirl checkout.whirl` and
`whirl --headed checkout.whirl` fail with a hint to use `whirl run`, and
`whirl` alone prints help.

`--out DIR` names the output directory, with the same default
`whirl-artifacts/`. `--artifacts` stays as a deprecated alias that warns
and cannot be combined with `--out`. `--load-state` and `--save-state`
replace `--storage` and `--save-storage`, which are now usage errors that
name their replacements. The state file keeps its format.

`--storage` silently replaced a file's `storage` option. `--load-state`
is a command control instead, so it cannot be combined with a file's
`storage` or `setup` option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`whirl run` and `whirl check` take repeatable `-O key=value` arguments
that set an `[Options]` key for every file, setup flows included.
Settings resolve from the built-in default, then the file, then the
command line. A value has the syntax of the file's `key: value` line, so
locators and quoted values keep their meaning, and an unknown key or
invalid value is a usage error before any browser starts.

For a scalar key the last value wins. For `allow-hosts` and
`snapshot-mask`, the values form a list that replaces the file's list,
and an empty value clears it. `-O setup=` is rejected, because setup
flows get the same options.

`--base`, `--browser`, `--step-timeout`, `--entry-timeout`, and
`--user-agent` now go through the same resolver, with their value taken
literally. Supplying one of them and `-O` for the same key is an error.

One module, lang::cli_options, owns this, so later commands can reuse
it. It puts the command line's lines in place of the file's, marked with
their source, so lint, the run, and reports read one list. `whirl check`
therefore lints with the overrides in place, and a lint about a `-O`
value has no line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`[Options] block-hosts` blocks the hosts its globs match, with the glob
rules of `allow-hosts`. A block wins over an allowance, including the
implicit allowance of the base host. The shim checks one host policy for
page requests, redirect hops, WebSockets, and `HTTP` entries, and it
routes requests when either list is set. `-O block-hosts=` forms and
clears a list like `allow-hosts`.

Each blocked host now carries the rule that blocked it: the matching
`block-hosts` glob, or `allow-hosts` when no allowed glob matched. The
console, JUnit, and HTML reports show it, and the JSON report adds
`blockedHostRules` beside the unchanged `blockedHosts`. This changes the
`endFlow` result, so the shim moves to protocol 9.

`browsersim-origin: build | recorded` is the first BrowserSim setting.
Ordinary runs validate it and do not apply it, and list it as inactive
when a file or `-O` sets it. Other `browsersim-` keys stay unknown.

Every file whose options resolve lists its settings in the JSON and HTML
reports: the value it ran with, masked like other output, where the value
came from, and whether this engine applies it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Later BrowserSim work will consume the settings rules of `whirl run`. This
draft ADR fixes what that work must follow: `whirl record` is the only
recording command, `--engine` and `--browser` stay separate, a
`*.sim.json` recording stores every effective setting and replay uses
only those, and each written flow records to a sibling bundle named after
its stem, with collisions and output-over-input paths rejected.

It is a draft: it becomes binding when it is accepted. Its two open
decisions, the privacy and matching option syntax and what happens when a
bundle exists, do not block the CLI work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `-O` value is now literal text, as if quoted, the way the option flags
already took theirs: `-O 'user-agent=Whirl/1 (test)'` works without inner
quotes, and `#` and `{{` are part of the value. For a list option, each
`-O` gives one item, so a host list written as in a file is a usage error
that says to give each host its own `-O`. A `snapshot-mask` value stays a
locator written as in a file. An empty value for a single-value option is
a usage error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`-O` now sets every option, so these four flags were an arbitrary subset
of the 17 keys. Each is now a usage error that names its `-O` form, such
as `-O base=URL`. `--browser` stays: the accepted recording ADR keeps it
beside `--engine`, and `whirl doctor --browser` names browsers the same
way.

Tests, the example script, and the docs use `-O` instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e41e403)
@brynary
brynary added this pull request to stack #49 October 1, 2026 20:57
@brynary
brynary merged commit 55320f0 into mock Oct 5, 2026
3 checks passed
@brynary
brynary deleted the cli-settings branch October 5, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant