Skip to content

feat: version-aware scenario loading and $.version in startup scenario#1959

Open
Copilot wants to merge 4 commits intomainfrom
copilot/update-scenario-loading-for-versions
Open

feat: version-aware scenario loading and $.version in startup scenario#1959
Copilot wants to merge 4 commits intomainfrom
copilot/update-scenario-loading-for-versions

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

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.

$.version in startup scenario

runStartupScenario now accepts a version string (default "") and passes it through the $ argument. Unversioned runners get version: "" — fully backwards compatible.

// scenarios/index.ts
export const startup: Scenario = ($) => {
  if ($.version === "v2") {
    $.context.featureFlags = { newPagination: true };
  }
};

Version-scoped scenario directory resolution

ModuleLoader accepts an optional versionedScenariosPath. On load():

  1. Shared scenarios from <group>/scenarios/ are loaded first.
  2. Version-specific scenarios from <group>/<version>/scenarios/ are loaded second — overriding any shared file with the same key.

ApiRunner derives the versioned path automatically from version when non-empty; no caller changes needed.

watch() and stopWatching() 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.tsstartup()) runs once per runner and is given a $ object with context, loadContext, and route helpers. 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 SpecConfig has a non-empty version, resolve scenario scripts from:

  1. <basePath>/<group>/<version>/scenarios/ (version-specific, searched first)
  2. <basePath>/<group>/scenarios/ (shared fallback)

When version is empty, use only <basePath>/<group>/scenarios/ as today.

Startup scenario $ argument

Pass $.version (the version string, or "" for unversioned runners) into the startup scenario's $ argument so authors can branch initialisation logic:

// scenarios/index.ts
export const startup = ($: { context: ; version: string }) => {
  if ($.version === "v2") {
    $.context.featureFlags.newPagination = true;
  }
};

.scenario REPL command

No 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

  • When version is set, scenario files in <group>/<version>/scenarios/ are loaded and take precedence over same-named files in <group>/scenarios/
  • Files in <group>/scenarios/ remain available as a shared fallback in multi-version mode
  • The startup scenario's $ argument includes a version property containing the runner's version string
  • The startup scenario's $ argument has version = "" for unversioned runners (backwards compatible)
  • Single-spec / single-version scenario loading is unchanged
  • Unit tests cover version-scoped directory resolution and the fallback behavior

Manual acceptance tests

  • When version is set on a spec, a scenario file in <group>/<version>/scenarios/index.ts is picked up and its functions are callable via .scenario
  • A same-named file in <group>/<version>/scenarios/ overrides the <group>/scenarios/ copy — the versioned function wins
  • A shared file in <group>/scenarios/ with no versioned counterpart remains accessible in a versioned runner
  • startup() receives $.version === "v2" for a versioned runner and can branch on it
  • startup() in an unversioned runner receives $.version === "" — existing behavior unchanged

Tasks

  • Added version: string to Scenario$ type; added version param (default "") to runStartupScenario() and pass primaryRunner.version at the call site in start()
  • Extended ModuleLoader with optional versionedScenariosPath; loadScenarios() now loads shared then versioned (versioned wins); watch()/stopWatching() manage a second chokidar watcher; scenarioFileKey()/loadScenarioFile() accept an optional base path
  • ApiRunner constructor derives version ? pathJoin(modulesPath, version, "scenarios") : undefined and passes it to ModuleLoader
  • Added 6 unit tests: 2 in test/app.test.ts ($.version forwarding), 5 in test/server/module-loader.test.ts (versioned loading, precedence, fallback, missing-dir resilience)
  • Updated docs/features/repl.md with version-scoped directory table and $.version startup example

@pmcelhaney pmcelhaney marked this pull request as ready for review April 26, 2026 01:51
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
Copilot AI requested a review from pmcelhaney April 26, 2026 01:57
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.

scenarios: version-aware scenario loading and startup scenario for multi-version APIs

2 participants