Bezgelor is tested by machines before it is tested by people. These reports are the results, published in full
so that anyone can see how far the server gets and check our claims. Every run uses test characters and bots
on a test server, never real players, so there is nothing private in them.
What the groups below are:
-
Day one (levelling): a new character is played from its starting zone towards level 15 by a headless
client, using the real game protocol. The report says how far it got, how many quests it finished, how
often it died and where it got stuck.
-
Combat audit and Quest audit: our rules checked behaviour by behaviour against what we know of the
retail game, with the ones that matched, the ones we fixed and the ones we have no evidence for.
-
Encounter simulator: five bot players fight a boss in a real zone instance. Read the kill times as a
relative measure, not a prediction of what a group of people would do; see
how we test instances.
-
Soak and load, Zone tick: how the server copes with many bots and big zones.
None of this replaces playing. If a report shows something that does not match how you remember the game, we
would like to hear it.
For developers
What follows describes the file formats and the commands that reproduce a run.
Day one
The day-one test (apps/bezgelor_world/test/e2e/day_one_test.exs, method in
docs/research/2026-10-11-day-one.md) creates a new character through the
real game protocol and plays it headless from its starting zone towards
level 15: quests, combat with the class's abilities, loot, equipment, public
events and path missions. Every packet the server sends is checked by the
WildStar client's own message parser. Strict mode uses no GM command but
teleports; fast mode also raises the level by GM command where content
runs out.
Files:
-
day-one-<class>-<mode>-<date>.json and .md - one run
-
day-one-history.json - one summary per published run, appended to by
each new run, so progress shows over time
To reproduce a run, use the command in its report. To publish one:
cd apps/bezgelor_world
DAY_ONE_CLASSES=warrior DAY_ONE_MODES=fast DAY_ONE_TIME_BUDGET=1500 \
LIMIT_HEAVY=1 ../../scripts/limited.sh mix test test/e2e/day_one_test.exs --include day_one
cd ../..
python3 tools/day_one/publish_report.py tmp/day_one/warrior-fast.json
Run report: schema bezgelor.day_one.v1
|
Field |
Meaning |
schema |
"bezgelor.day_one.v1" |
date, commit |
when, and the commit the run ran on |
class, faction, mode, start |
the character (start: veteran = the faction's level 3 start on Nexus, novice = the New Player Experience) and the mode (strict or fast) |
target_level, time_budget_s, oracle |
the run's settings (oracle: packets checked by the client's parser) |
runtime_s, result, final_level, deaths |
the outcome (result: reached_target or stopped) |
quests_completed, quests_attempted |
counts |
levels |
[{level, at_s}], seconds from the start of the run |
worlds |
[{world_id, at_s, level}] worlds entered |
quests |
[{id, title, level, state, accepted_at_s, completed_at_s, duration_s}] (state: the last event: offered, accepted, achieved, completed) |
blockers |
[{kind, count, quest, quest_title, objective, level, world, at_s, cause, detail, issue}]: each point where progress stopped, with its GitHub issue on Bezgelor/bezgelor when one is filed |
counters |
kills, deaths, casts, loot, activations, teleports, ... |
notes |
the run's decisions and context |
command |
the command that reproduces the run |
New fields may be added; existing ones keep their meaning. A change that
breaks that gets a new schema version.
History: schema bezgelor.day_one.history.v1
{schema, runs: [summary]}, one summary per run: date, commit, class,
faction, mode, start, target_level, time_budget_s, runtime_s,
result, final_level, deaths, quests_completed, quests_attempted,
blockers (count), issues (the issue numbers its blockers point to),
report (the run's file). A run with the same date, class, mode and commit
replaces its earlier entry.
Encounter simulator
The encounter simulator (BezgelorWorld.EncounterSim, method in
docs/research/2026-10-12-stormtalons-lair.md) runs five bot players (tank,
healer, three damage dealers) against one boss in a real zone instance: the
instance's spawns and scripts, every ability through the spell pipeline. Its
reports tune bosses (Level 3 of the master plan's dungeon fidelity).
Files: <instance>-sim-<date>.json (schema bezgelor.encounter_sim.v1,
documented in BezgelorWorld.EncounterSim.Report) and .md. To reproduce,
use the command in the report, for example:
cd apps/bezgelor_world
SIM_RUNS=2 SIM_REPORT=../../docs/community/test-reports/stormtalons-lair-sim-2026-10-13 \
LIMIT_HEAVY=1 ../../scripts/limited.sh mix test test/encounter_sim/stormtalons_lair_sim_test.exs --include encounter_sim
Coverage audit and coverage sweep
The audit and the sweep answer "does every level 1-15 quest, path mission and quest item work through the real
server", per quest and per objective, as a generated worklist (zones in BezgelorWorld.Sweep.Scope.zones/0).
-
Coverage audit (static, no server, seconds): coverage-audit-<date>.json (schema
bezgelor.coverage_audit.v1) and .md. Every quest objective, path mission and quest item is checked
against the server's code paths. A quest is blocked (a player cannot finish it), at_risk (the
mechanism exists but something it relies on, such as a spell effect type, is missing) or ok. Every reason
has a code (no_rule, target_no_spawn, giver_no_spawn, receiver_no_spawn, no_giver,
item_start_unhandled, accept_prereq_unhandled, activate_prereq_unhandled,
activate_spell_unhandled_effect, quest_ability_unhandled_effect, pushed_item_missing,
item_spell_unhandled_effect, path_no_plan, path_no_spawn, ...), a severity, a detail and, where
useful, labels (objective type or effect type). JSON: {schema, scope, quests, items, path_missions, summary}; summary.reasons ranks the reasons by quests affected.
cd apps/bezgelor_world
../../scripts/limited.sh mix test test/sweep/audit_test.exs --include audit
SWEEP_ZONE=algoroc,celestion and SWEEP_QUEST=4023,4024 narrow it; SWEEP_REPORT sets the output base name.
-
Coverage sweep (dynamic, through the real listeners): coverage-sweep-<scope>-<date>.json (schema
bezgelor.coverage_sweep.v1) and .md. Every quest the audit does not mark impossible is played by a fresh
headless character: accepted at a live giver (earlier quests the quest requires are written as completed first
and listed as seeded), each objective done beside its target (GM teleport for travel, GM kill for kills,
activation / talk / quest ability / marker by client message), then handed in. Per quest a status
(pass, fail, skipped), checks (accept, pushed_item, achieved, turn_in, item_use ... cast and
consumed) and per objective checks (progress, progress_update = the client was sent the objective update,
no_double_count = a second activation of the same unit did not count again). A fail says what was seen; a skip
says why. JSON: {schema, meta, summary, results}; summary.failure_reasons ranks the failing steps by quests.
cd apps/bezgelor_world
SWEEP_ZONE=northern_wilds LIMIT_HEAVY=1 ../../scripts/limited.sh mix test test/e2e/coverage_sweep_test.exs --include sweep
SWEEP_ZONE (comma list of zone keys), SWEEP_QUEST (ids), SWEEP_FACTION, SWEEP_LIMIT, SWEEP_BUDGET_S
(per quest, default 240), SWEEP_ORACLE=1 (check every packet with the client's parser; slow) and
SWEEP_REPORT narrow or direct a run. The sweep and the audit are excluded from mix test by default
(tags :sweep and :audit).
After committing a new audit or zone sweep, rebuild the Workshop's status page from them:
python -I tools/status/build.py (the portal's docs test fails while it is stale).