Skip to content

Run against a base URL that scopes paths and pins query params - #231

Open
DavertMik wants to merge 7 commits into
mainfrom
run-base-url
Open

DavertMik wants to merge 7 commits into
mainfrom
run-base-url

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Problem

A plan written against /runs cannot be run inside one project of a multi-tenant app. In global mode enterGlobalMode forces playwright.url to the site origin, so every relative path resolves at the root — /runs, never /projects/acme/runs. Putting the prefix in the base URL instead is not enough either: CodeceptJS joins options.url + path by concatenation, so the navigator then compares an expected /runs against an actual /projects/acme/runs and asks the AI to recover from a page it just loaded correctly.

Selecting a preview deployment by query parameter (?assets_host=…, ?preview=…) had no mechanism at all: any relative navigation drops the query.

What this adds

--base-url <url>, a common option on every browser command:

  • origin — selects the site as before, so the site's config, knowledge and experience apply unchanged
  • path — prefixed onto relative targets from plans and commands (/runs/teams/acme/runs), idempotent, and never applied to absolute URLs so a login page outside the scoped area still works
  • query — added to every document request to that origin, so a preview deployment stays selected for the whole run
explorbot test plans/runs.md '*' \
  --base-url 'https://app.example.com/teams/acme/?preview=pr-42'

How it works

ConfigParser parses the flag once into {origin, path, query}: the origin becomes playwright.url (so CodeceptJS keeps joining against a clean origin), applyBasePath() owns the prefix rule, and getBaseQuery() exposes the params. Two seams call it — resolveTargetPath() for command targets, Explorer.visit() for the browser boundary — and setCurrentPlan() scopes a loaded plan's URLs in memory, so per-test start URLs, the reset tool and the AI's context all agree on the same URL. The plan file on disk is never rewritten.

The query is pinned with a Playwright route on the browser context that rewrites document requests only. The param reaches the server without entering the address bar, so state hashes, experience files and generated tests stay free of it.

Verification

  • bun test tests/unit/ — 1448 pass, biome format + lint clean, tsc clean on changed files
  • New unit test covers scoping, idempotency, absolute-URL passthrough and the origin-only playwright.url
  • Checked against a live app: landing on a deep scoped URL delivered the preview assets, and navigation afterwards kept them

Not covered by unit tests: the route rewrite itself (needs a browser). Worth a regression label if you want it exercised end to end — I have not applied one.

🤖 Generated with Claude Code

DavertMik and others added 7 commits September 14, 2026 14:46
Extracts src/utils/markdown-query.ts into src/utils/mdq/ as a
publish-ready package: MarkdownDoc + Selection, insert/remove verbs
alongside query, a comment selector, frontmatter handling, JS-value
matchers, and a planned jq-like CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
Frontmatter uses yaml's Document API (comment-preserving) rather than a
hand-rolled parser; MarkdownDoc gains append/prepend; leading '.' is
accepted in the CLI grammar; documents a fourth migration breakage class
where MarkdownDoc === string silently stops a guard from firing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
11 tasks, 68 steps. Ordering keeps the repo green at every boundary:
port behind a shim first, migrate the 11 write-return-type breaks second,
then add features additively.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhJNfxLquFknSLJ6V8J9iD
…rams

A plan written against `/runs` could not be run inside one project of a
multi-tenant app: in global mode the browser base URL is forced to the site
origin, so a relative path always resolved at the root. Selecting a preview
deployment by query parameter had no mechanism at all.

`--base-url <url>` gives a run one base. The origin still selects the site, so
its config, knowledge and experience apply unchanged; the path is prepended to
relative targets coming from plans and commands; the query params are added to
every document request to that origin. Absolute URLs are never prefixed, so a
login page outside the scoped area still works, and the params travel on the
request rather than the address bar, keeping states, experience and generated
tests free of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread src/config.ts
if (this.config && !options?.config && !options?.path && this.runtimeTarget === target) {
return this.config;
}
this.runBase = parseRunBase(options?.baseUrl);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In config-free mode, EXPLORBOT_URL still wins when selecting the output root. With both values set, --base-url changes the browser origin but knowledge, experience, and output can come from the wrong site

Comment thread bin/explorbot-cli.ts
.option('--debug', 'Enable debug logging (same as --verbose)')
.option('-c, --config <path>', 'Path to configuration file')
.option('-p, --path <path>', 'Working directory path')
.option('--base-url <url>', 'Run against this URL: its path scopes relative paths from plans, its query params ride along with every page load')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also exposes --base-url to rerun, but rerun executes saved I.amOnPage() calls directly through Mocha, bypassing the new path-scoping logic

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.

2 participants