feat: version-aware scenario loading and $.version in startup scenario#1959
Open
feat: version-aware scenario loading and $.version in startup scenario#1959
Conversation
6 tasks
Copilot
AI
changed the title
[WIP] Update scenario loading to support version-aware directories
feat: version-aware scenario loading and $.version in startup scenario
Apr 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scenarios lacked version-awareness: all runners loaded from a single
scenarios/directory, and the startup scenario had no way to know which version it was initializing.$.versionin startup scenariorunStartupScenarionow accepts aversionstring (default"") and passes it through the$argument. Unversioned runners getversion: ""— fully backwards compatible.Version-scoped scenario directory resolution
ModuleLoaderaccepts an optionalversionedScenariosPath. Onload():<group>/scenarios/are loaded first.<group>/<version>/scenarios/are loaded second — overriding any shared file with the same key.ApiRunnerderives the versioned path automatically fromversionwhen non-empty; no caller changes needed.watch()andstopWatching()are updated to track the second watcher.Original Prompt
Update the scenario system so that each versioned API runner loads scenarios from a version-scoped directory, and the startup scenario receives version information so it can initialise per-version state.
Context
Scenarios are currently loaded from
<basePath>/<group>/scenarios/. When a group has multiple versions (v1,v2, …), scenarios that are specific to one version should live under<basePath>/<group>/<version>/scenarios/while shared scenarios can remain at<basePath>/<group>/scenarios/.The startup scenario (
scenarios/index.ts→startup()) runs once per runner and is given a$object withcontext,loadContext, androutehelpers. In multi-version mode, the startup scenario may also need to know which version it is initialising.Proposed change
Scenario directory resolution
When a runner's
SpecConfighas a non-emptyversion, resolve scenario scripts from:<basePath>/<group>/<version>/scenarios/(version-specific, searched first)<basePath>/<group>/scenarios/(shared fallback)When
versionis empty, use only<basePath>/<group>/scenarios/as today.Startup scenario
$argumentPass
$.version(the version string, or""for unversioned runners) into the startup scenario's$argument so authors can branch initialisation logic:.scenarioREPL commandNo changes are needed here beyond those tracked in the REPL version-awareness issue; scenario loading uses the same directory resolution rules described above.
Acceptance criteria
versionis set, scenario files in<group>/<version>/scenarios/are loaded and take precedence over same-named files in<group>/scenarios/<group>/scenarios/remain available as a shared fallback in multi-version mode$argument includes aversionproperty containing the runner's version string$argument hasversion = ""for unversioned runners (backwards compatible)Manual acceptance tests
versionis set on a spec, a scenario file in<group>/<version>/scenarios/index.tsis picked up and its functions are callable via.scenario<group>/<version>/scenarios/overrides the<group>/scenarios/copy — the versioned function wins<group>/scenarios/with no versioned counterpart remains accessible in a versioned runnerstartup()receives$.version === "v2"for a versioned runner and can branch on itstartup()in an unversioned runner receives$.version === ""— existing behavior unchangedTasks
version: stringtoScenario$type; addedversionparam (default"") torunStartupScenario()and passprimaryRunner.versionat the call site instart()ModuleLoaderwith optionalversionedScenariosPath;loadScenarios()now loads shared then versioned (versioned wins);watch()/stopWatching()manage a second chokidar watcher;scenarioFileKey()/loadScenarioFile()accept an optional base pathApiRunnerconstructor derivesversion ? pathJoin(modulesPath, version, "scenarios") : undefinedand passes it toModuleLoadertest/app.test.ts($.versionforwarding), 5 intest/server/module-loader.test.ts(versioned loading, precedence, fallback, missing-dir resilience)docs/features/repl.mdwith version-scoped directory table and$.versionstartup example