Adding a Scenario¶
A scenario is a story arc with its own prologue, namespace, and challenge sequence. Oakwood Meadows and Signal Town are the two scenarios today. Use this guide when adding a third region (for example a Deployment-focused town).
Overview¶
scenarios/<new_scenario>/
├── prologue/
│ ├── screens/
│ ├── dialogues/
│ └── k8s_resources/
│ └── namespace.yaml
└── challenge_<N>/
└── ... (same layout as existing challenges)
You will also wire the scenario into progress, story intros, and CHALLENGE_SCENARIO_MAP.
1. Plan the arc¶
Decide:
- Scenario folder name (snake_case, for example
iron_peak) - Kubernetes namespace string (for example
iron-peak) - Challenge ID range (must not overlap unless you replace an arc)
- Prologue characters and screens
- Which concepts the arc teaches
Open an issue before large additions.
2. Create prologue infrastructure¶
namespace.yaml¶
apiVersion: v1
kind: Namespace
metadata:
name: iron-peak
Place under scenarios/<new_scenario>/prologue/k8s_resources/.
Bootstrap hook¶
Prologue namespaces are applied by resource_manager.apply_prologue_resources(scenario_name).
Choose when to call it:
- Option A: On a dedicated intro screen's
on_mount(Signal Town pattern) - Option B: During game initialisation when entering the arc (Oakwood pattern)
Example (intro screen):
async def _bootstrap_iron_peak_infra(self) -> None:
await asyncio.to_thread(
resource_manager.apply_prologue_resources, "iron_peak"
)
Ensure the cluster is running before apply.
3. Prologue screens and dialogues¶
Add under:
scenarios/<new_scenario>/prologue/screens/
scenarios/<new_scenario>/prologue/dialogues/
Follow existing intro screens for structure:
scenarios/oakwood_meadows/prologue/screens/professor_bald_intro.pyscenarios/signal_town/prologue/screens/signal_town_intro_screen.py
Wire navigation from the previous arc's success path or a new story_intro_act value in global_constants.py and general_utils.load_story_intro_screen().
4. Add challenges¶
For each challenge in the arc, follow Adding a Challenge.
Use the new namespace in all manifests and validators. Add a constant:
NAMESPACE_IRON_PEAK = "iron-peak"
in challenge_files/challenge_constants.py (or a future scenario-local constants module).
5. Register challenge IDs¶
Update CHALLENGE_SCENARIO_MAP in utils/general_utils.py:
"14": "iron_peak",
"15": "iron_peak",
Update TOTAL_CHALLENGES in global_constants.py.
6. Lab workspace¶
No extra code needed - ensure_lab_workspace() glob-copies all scenarios/*/challenge_*/k8s_resources/* into yellow-olive-lab/.
Verify a fresh lab picks up your new manifests.
7. Package discovery¶
Ensure pyproject.toml still includes the scenarios* package (it does today via setuptools find).
Run:
pip install -e .
python -c "import scenarios.iron_peak.challenge_14.screen"
8. Story and progress integration¶
Depending on how the arc enters the campaign, you may need to update:
| File | Change |
|---|---|
global_constants.py |
New STORY_ACT_* values if multi-screen prologue |
utils/general_utils.py |
load_story_intro_screen(), CHALLENGE_SCENARIO_MAP |
screens/common/base_challenge_screen.py |
Transition from previous arc's last challenge |
screens/common/psy_quack_success_screen.py |
Load next challenge or intro screen |
Keep transitions consistent with how challenge 7 hands off to Signal Town.
9. Shared UI¶
Cross-scenario screens stay in screens/common/. Only put content in scenarios/<name>/ if it is specific to that arc.
10. Documentation¶
Update:
Testing checklist¶
- [ ] Namespace applies on prologue entry
- [ ] Each challenge applies lab manifests in the new namespace
- [ ] Validators use the new namespace constant
- [ ] Resume mid-arc loads correct screen
- [ ] PyPI / editable install includes new package paths