Skip to content

Let the plan name the site an explorbot test run explores - #165

Merged
DavertMik merged 6 commits into
mainfrom
fix/test-plan-names-site
Aug 31, 2026
Merged

Let the plan name the site an explorbot test run explores#165
DavertMik merged 6 commits into
mainfrom
fix/test-plan-names-site

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Running a saved plan against a global installation was impossible: the command died in config loading before it ever opened the plan.

$ explorbot test ~/plans/pr-3625-run-user-rendering.md
Configuration loaded from: /home/davert/.explorbot/config.js
Failed to start: Failed to load configuration: Error: No site to explore. Pass a URL to the command or set EXPLORBOT_URL.

~/.explorbot/config.js holds models and keys, never a site — every command names the site it runs against. test named none: it built its ExplorBot with from: undefined, so enterGlobalMode had no target. The plan carries the URL all along, in its ### Prerequisite section, so the CLI now reads it before the container is built and passes it as the run target. The same target feeds the EXPLORBOT_* branch of config loading, so a config-free env setup gets the fix too.

src/commands/test-command.ts is unchanged — the gap was in bootstrapping, not in test selection.

Also here:

  • explorbot test <planfile> with no index runs every enabled test, as --help and the docs already said. It used to run only the first pending one.
  • A plan's URL — its own, else the first test's — becomes Plan.startUrl, replacing the four copies of the expression (CLI twice, both historian writers).

Verification

Smoke run against a real plan with an out-of-range index, so nothing executed past navigation:

Global mode: https://beta.testomat.io stored in ~/.explorbot/sites/beta.testomat.io
Plan loaded: "Run assignee rendering and actions" (8 tests, 8 pending)
Navigating to https://beta.testomat.io/projects/.../runs
Navigation resolved successfully
Failed: Test #99 not found. Available: 1-8

bun test on test-plan, historian, config and global-config: 97 pass.

Notes

  • One error-path change: a plan with no Prerequisite URL whose tests are all finished now navigates first and reports "All tests are already complete" instead of "No URL found in plan or tests".
  • rerun <file> has the same gap and is left alone here.

🤖 Generated with Claude Code

https://claude.ai/code/session_013W3pakMDZuDEjzm6LCFMCB

`explorbot test <planfile>` built its ExplorBot with no target, so a
global installation had nothing to resolve a site from and the run died
in config loading with "No site to explore" before the plan was ever
read. The plan already carries the URL: the CLI now reads it before the
container is built, and passes it as the run target, which serves both
the global installation and the EXPLORBOT_* variables.

The expression for a plan's URL — its own, else the first test's — moves
onto Plan as `startUrl`, replacing the four copies of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013W3pakMDZuDEjzm6LCFMCB
Comment thread bin/explorbot-cli.ts Outdated
indexArg = planfile;
}

const planFile = [planfileArg, `${planfileArg}.md`].find((file) => fs.existsSync(file));

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 only checks the current directory, while loadPlan() also resolves names through getPlansDir(). In global mode, explorbot test saved-plan can therefore fail before loadPlan() even though the plan exists. Could both paths use the same resolver?

DavertMik and others added 5 commits August 31, 2026 23:37
`explorbot test <planfile>` peeked at the plan for its URL with an
existence check against the working directory alone, while `loadPlan()`
resolves a name through the plans directory as well. In a global
installation a saved plan named by its file name was therefore invisible
to the peek, and the run died in config loading with "No site to
explore" before `loadPlan()` could find it.

Both paths now call one resolver, `resolvePlanPath()` in
`src/utils/plan-path.ts`: a path or a name, `.md` optional, looked up in
the working directory and then in the plans directory. The CLI runs
before the config is loaded and has no plans directory to name, so it
searches the plans directory of every registered site instead — enough
to read the plan, take its URL, and let config loading resolve the site
from it. `ExplorBot.resolvePlanPath()` keeps its signature and delegates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCCFv7WWR67iCLLkSnnMnS
The resolver belongs with the class that already reads plan files, not
in a file of its own: `Plan.resolvePath()` beside `Plan.fromMarkdown()`,
with its tests in the Plan suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCCFv7WWR67iCLLkSnnMnS
`Plan.loadFromFile(file, plansDir?)` resolves a path or a saved-plan
name and returns the plan it loaded, or null when there is none — no
separate path resolver to call first. It records the file it read as
`plan.filePath`, so a caller that wants the file (the `/plans` listing,
the multi-suite read behind `explore --configure`) takes it from the
plan instead of resolving twice.

`ExplorBot.resolvePlanPath()` is gone; `loadPlan`, `loadPlans`,
`PlansCommand` and both CLI plan commands go through the loader.
`plan:load` resolves saved-plan names now too, as `test` does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCCFv7WWR67iCLLkSnnMnS
@DavertMik
DavertMik merged commit 86cbc04 into main Aug 31, 2026
2 checks passed
@DavertMik
DavertMik deleted the fix/test-plan-names-site branch August 31, 2026 23:40
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