Adding a Challenge¶
This guide replaces the old README contributing steps. Follow it when adding a new challenge to an existing scenario (Oakwood Meadows or Signal Town).
Before you start¶
- Open a GitHub issue describing the learning objective and Kubernetes concept.
- Pick the scenario folder and next challenge number.
- Run the game from source so you can test the full flow.
Checklist¶
- [ ] Create challenge folder with four files + manifests
- [ ] Add constants to
challenge_files/challenge_constants.pyif needed - [ ] Register in
CHALLENGE_SCENARIO_MAPand bumpTOTAL_CHALLENGESif extending the campaign - [ ] Test: apply, fix in Command Chamber,
psyquack validate, success screen, next challenge - [ ] Update docs or scenario table if the challenge list changes materially
1. Create the folder¶
scenarios/<scenario>/challenge_<N>/
├── screen.py
├── challenge_text.py
├── validator.py
└── k8s_resources/
├── pod-q<N>.yaml
└── svc-q<N>.yaml # optional, for Service challenges
Use existing challenges as templates:
- Pod-focused: copy from
scenarios/oakwood_meadows/challenge_1/ - Service-focused: copy from
scenarios/signal_town/challenge_8/
2. challenge_text.py¶
Export a string constant with the story setup:
CHALLENGE_14_TEXT = """
Your narrative setup here.
Explain what broke in Yellow Olive terms.
"""
Keep tone consistent with nearby challenges in the same scenario.
3. screen.py¶
Minimum implementation:
from scenarios.<scenario>.challenge_<N> import challenge_text
from screens.common.base_challenge_screen import BaseChallengeScreen
from services import resource_manager
class Challenge<N>(BaseChallengeScreen):
challenge_id = "<N>"
challenge_scenario = "<scenario>"
challenge_text = challenge_text.CHALLENGE_<N>_TEXT
def create_resources_for_challenge(self):
resource_manager.apply_manifest(self.challenge_scenario, self.challenge_id)
Optional: customise __init__ to append the lab manifest path (see challenge 1).
4. k8s_resources/¶
Write deliberately broken YAML that teaches the target concept. Use the correct namespace in metadata:
- Oakwood Meadows:
namespace: oakwood-meadows - Signal Town:
namespace: signal-town
File naming convention:
pod-q<N>.yamlsvc-q<N>.yaml
Players receive copies under yellow-olive-lab/scenarios/<scenario>/challenge_<N>/k8s_resources/.
5. validator.py¶
Implement validate() returning (bool, str). Use resource_inspector only.
Pattern:
from challenge_files import challenge_constants
from services import resource_inspector
def validate():
# 1. Resource exists
# 2. Spec fields
# 3. Runtime state (ready, endpoints, etc.)
return True, "In-character success message"
See Validation for API details and examples.
6. Constants¶
Add shared names to challenge_files/challenge_constants.py:
CHALLENGE_14_POD_NAME = "my-pod"
CHALLENGE_14_SERVICE_NAME = "my-service"
Use existing namespace constants (NAMESPACE_OAKWOOD_MEADOWS, NAMESPACE_SIGNAL_TOWN) where possible.
7. Register the challenge¶
In utils/general_utils.py:
CHALLENGE_SCENARIO_MAP = {
...
"<N>": "<scenario>",
}
If <N> is the new final challenge, update TOTAL_CHALLENGES in global_constants.py.
8. Story transitions (optional)¶
Most challenges advance automatically via get_next_challenge_id. Special cases need edits in BaseChallengeScreen.handle_validation() - for example challenge 7 sets story_intro_act before Signal Town.
Only touch that code if your challenge ends an arc or starts a new prologue.
9. Test locally¶
# From repo root
python -c "from utils.general_utils import load_challenge; load_challenge('<N>')"
# Manual validator test (cluster must be running, challenge applied)
python -c "from scenarios.<scenario>.challenge_<N>.validator import validate; print(validate())"
# Full game
python app.py
Walk through:
- Challenge loads and applies manifests
- Broken state is reproducible
- Your fix passes validation
- Wrong states fail with helpful messages
- Success screen and next challenge (or story transition) work
10. Pull request¶
Include in the PR description:
- Learning objective (what Kubernetes skill this teaches)
- Scenario and challenge number
- Short screen recording or terminal capture of pass and fail paths
- Any new constants or namespace assumptions
Common mistakes¶
| Mistake | Fix |
|---|---|
Validator checks default namespace |
Use scenario namespace constants |
Forgot challenge_scenario on screen |
Set attribute on the screen class |
| Manifest only in repo, not lab | Game copies on first run - delete lab copy to refresh |
| Validator shells out to kubectl | Use resource_inspector |
| Success possible without intended fix | Tighten checks (endpoints, not just Service exists) |
Related pages¶
- Adding a Scenario - new town / arc
- Validation - inspector API
- Lab Workspace - where players edit YAML