Mission authoring¶
Mission content is declarative JSON embedded into the OpsQuest binary from internal/mission/data. Go code owns parsing, validation, execution, and outcome observation; mission files describe one exercise.
Author around evidence¶
A mission defines its initial world and the evidence of success. Validation should answer “what must now be true?” rather than “which command did the player type?”
Good evidence includes:
- a file exists at one path and no longer exists at another;
- file content, mode, or owner matches the intended result;
- a target process is stopped while a healthy process remains running;
- output contains all relevant paths and excludes a distractor;
- a report contains exactly the required logical lines;
- an attempt-owned Docker container is in the required state, or has been removed.
Avoid requiring one command name, argument order, or intermediate state when another supported solution demonstrates the same understanding.
Before adding or moving a mission, answer:
- Which prior observations or command behaviors does it assume?
- What concept is introduced rather than merely repeated?
- Which observable outcomes prove the objective?
- Which equivalent supported solutions should remain valid?
- Where will the concept return with greater complexity?
- Can a player understand incomplete progress and restart safely?
Mission shape¶
Each mission defines:
identity: stable ID, global number, track, environment, campaign
narrative: title, difficulty, story, objective, explanation
guidance: suggested command names and one to five progressive hints
setup: virtual directories, files, processes, environment, and archives
or bounded Docker image and container fixtures
validation: one or more observable outcome conditions
rewards: base XP and per-hint penalty
Linux remains the default track and simulated remains the default environment for compatibility with earlier mission files. Docker missions must use the Docker track and cannot mix simulated setup with Docker fixtures.
Strict catalog loading¶
mission.LoadCatalog loads every embedded JSON file before the CLI starts. It rejects:
- unknown JSON fields or multiple JSON values;
- malformed or duplicate stable IDs and global numbers;
- non-contiguous global numbering;
- unsupported tracks, environments, or difficulties;
- missing narrative, guidance, setup, validation, or reward data;
- unsafe, conflicting, or inconsistent virtual paths and state;
- validators with missing, extra, or incompatible fields;
- unpinned Docker images, invalid aliases, oversized Docker setup, or inconsistent fixture behaviors;
- a campaign that reappears as separate worlds in one track.
Catalog access is indexed by ID and number. Returned missions and worlds are deep copies so adapters cannot mutate embedded content.
Observable validation¶
Conditions cover output, working directory, path existence, file content and logical lines, modes, owners, process state, environment values, and bounded Docker container state. Docker fixtures select fixed behaviors implemented in Go rather than supplying commands:
| Fixture fields | Behavior | Rules |
|---|---|---|
| none | Long-lived service | running or stopped |
log |
Long-lived service that prints a startup log | Log at most 8 KiB, no NUL |
health: healthy or unhealthy |
Service with a fixed passing or failing health probe | Not allowed on exiting fixtures; setup waits for a running probe to settle |
log + exit_code |
One-shot diagnostic job | Must be stopped; setup runs it to completion |
log + non-zero exit_code + restart: on-failure |
Bounded crash loop (on-failure:50) |
Must be running; setup waits until at least two restarts are visible |
networks: [...] |
Joins declared attempt networks at creation | Up to 4 declared networks; without it, networking is disabled and the container cannot join a network later |
A Docker setup can declare up to 8 networks by logical name ({"name": "db-net"}). Names follow the logical-name rules and cannot be bridge, host, none, or default. OpsQuest creates every declared network as an internal network before the containers.
Container conditions can require running or stopped state (both require the container to still exist), or docker_container_absent after the player removes it. Network conditions are route-independent: docker_containers_share_network and docker_containers_isolated take exactly two containers and pass only while both still exist, and docker_network_absent takes a declared network. Prefer shared or isolated pairs over naming a specific network, so a player who creates their own network also succeeds. Every container or network a condition names must be declared in the fixture setup. The game layer compares output conditions; the active environment observes state conditions through the shared Environment contract.
Suggested commands identify the intended tool family but do not constrain validation. One to five hints should progress from concept, through inspection strategy, toward concrete syntax. Difficulty should reflect reasoning and composition rather than missing documentation.
Worlds and placement¶
Within each track, every contiguous campaign becomes an ordered world. Stage positions are derived at catalog load rather than persisted in JSON. Linux and Docker therefore have independent world numbering while mission numbers remain global.
Compatibility-sensitive mission data includes:
- stable mission IDs used by profile completions and hints;
- global mission numbers used by public top-level navigation;
- track and campaign order used to derive worlds;
- condition names and allowed fields;
- environment/setup pairing.
Changing one requires explicit compatibility reasoning and canonical success plus incomplete-solution coverage.
Required repository evidence¶
Every mission change needs canonical success coverage plus an incomplete or incorrect solution. Catalog tests must continue rejecting malformed fields, unsafe paths, conflicting setup, unsupported validators, and incompatible environment data.
The implementation lives in
internal/mission.
Use the repository's $add-mission workflow for the complete authoring and
validation sequence.