Screeps Lab
Screeps Lab is an owned, accelerated private Screeps environment for testing, scenario execution, regression checks, agent-driven code generation, telemetry, and artifact collection.
The official private-server stack is validated on Node 22; use the repository's
.nvmrc. The legacy Loki/backend failure has been observed under both Node 22
and Node 24, so Node 24 is not established as its root cause; Node 22 remains
the supported and validated runtime for this official-server stack.
@screeps/driver also contains a generated, V8-version-specific runtime
snapshot. Scenario and suite commands verify it in a child process and
regenerate it only when missing or incompatible. This internal binary is not an
MMO JSON world snapshot and does not authorize changing artifacts/incoming.
The operator checks and manual recovery path are:
npm run driver-snapshot:verify
npm run driver-snapshot:prepare
The prepare command uses the active Node runtime; it does not invoke or depend
on nvm.
This repository is separate from the primary Screeps colony codebase. The colony repository contains bot code. Screeps Lab contains the environment and orchestration system used to test colony code and related packages before they are trusted elsewhere.
The project is in its early implementation slices. It can launch an official private Screeps server through an internal server adapter, preserve or reset the private world, advance deterministic controlled ticks, keep the world open for interactive play, observe server lifecycle/output/tick events, provision a local subject player, install a tiny synthetic subject, observe that subject through Screeps memory, shut the server down cleanly, and leave run artifacts for inspection. It can also execute low-level private-server console expressions through the server adapter and, when explicitly enabled for play mode, run typed Creative Mode commands that create or modify supported objects in the local private world. It can also build an external local Screeps repository, install its generated modules into a selected private-server player and branch, advance one controlled tick, and verify that the external entry point executed. It can also prepare a deterministic owned private room with a player, controller RCL, spawn, sources, mineral, active-room metadata, controlled-tick verification, and machine-readable bootstrap artifacts. It now includes a minimal Scenario Engine foundation that runs bounded scenarios, separates engine execution from scenario outcome, writes terminal reports, and validates representative pass, assertion-fail, subject-error, and timeout paths. It can also qualify an exact clean external Git revision against an explicitly selected MMO snapshot, preserving the one built module map as an immutable local candidate artifact. Qualification never uploads or deploys to the MMO. Each attempt records two identities: deterministic checksums of the direct subject build before Lab instrumentation, and checksums of the exact qualified module map after the unique execution wrapper is added. Separate candidates may therefore have different qualified bytes even when their subject-build identity matches.
Release-candidate qualification:
npm run candidate:qualify -- --subject-path ../screeps \
--expected-revision 9247cac2cc68ca5b2a5fd6a33834db994adfed03 \
--suite snapshot-smoke \
--snapshot artifacts/incoming/screeps/snapshots/latest/snapshot.json --reset
npm run candidate:inspect -- latest
See Release Candidate Qualification.
Three-Party Model
Screeps Lab coordinates three major parties:
- Private Screeps server: runs the simulated world, advances ticks, and exposes console, memory, world-state, and deployment access.
- Subject codebase: the Screeps bot or package being tested. Initially this
will often be a sibling checkout such as
../screeps. - Overlord: the working title for the orchestration component. Overlord prepares scenarios, resets world state, deploys subject code, observes game state, evaluates assertions, and collects logs, telemetry, screenshots, reports, and other artifacts.
Overlord is a working title, not necessarily the final product name.
Repository Strategy
Screeps Lab is a single monorepo. It must not contain nested Git repositories, committed checkouts of the primary Screeps colony repository, or Git submodules unless a future architectural decision explicitly changes that policy.
Expected local development may look like this:
~/projects/
├── screeps/
└── screeps-lab/
Future subject sources may include external local paths, ignored local checkouts
under subjects/local/, temporary clones, mounted directories, or Git-based
subject adapters.
Repository Map
apps/ Independently runnable processes.
packages/ Reusable internal libraries and shared contracts.
scenarios/ Committed scenario definitions and fixtures.
subjects/ Documentation, examples, and ignored local subject workspaces.
config/ Checked-in defaults and future schemas.
scripts/ Repository-level development and operational scripts.
docs/ Deeper architecture, guide, and context-export documentation.
artifacts/ Generated reports, logs, screenshots, and replays.
tests/ Repository-wide integration and end-to-end tests.
See ARCHITECTURE.md and docs/architecture/repository-layout.md for more detail.
Quick Start
Prerequisites:
- Linux
- Node.js 22.9.0 or newer
- npm 10.8.2 or newer
- Python 3 and node-gyp build tools
- A C++20-capable compiler, such as
g++-10or newer
On older Ubuntu hosts where g++ is older than 10, install a newer compiler and
run npm with CXX=g++-10 or make the newer compiler the default.
First complete local validation:
npm run first-world
That single command installs dependencies, starts the private server, preserves any existing world, advances one tick, shuts the server down, and reports success. To explicitly recreate the private world:
npm run first-world -- --reset
The lower-level commands are:
npm install
npm run snapshot:pull
npm run sandbox:inspect -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json
npm run sandbox:import -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --reset --username <local-name> --steam-id <id>
npm run play
npm run play -- --creative
npm run play -- --paused
npm run play -- --tick-rate 250
npm run play -- --tick-rate max
npm run play -- --reset
npm run lab:eval -- "Game.time"
npm run lab:eval -- "Object.keys(Game.rooms)"
npm run lab:console
npm run smoke
npm run smoke -- --reset
npm run world:bootstrap -- --room E48N13 --username DaelinSolo --rcl 5 --spawn Spawn1
npm run world:bootstrap -- --room E48N13 --username DaelinSolo --rcl 5 --spawn Spawn1 --reset
npm run subject:smoke
npm run subject:smoke -- --reset
npm run subject:deploy -- --path ../screeps
npm run subject:status
npm run scenario:run -- 0.3.1-pass
npm run scenario:run -- external-subject-smoke --subject-path ../screeps --ticks 100
npm run scenario:smoke
npm run lab:start
npm run smoke does not reset the world by default. Use -- --reset only when
you intentionally want to delete and recreate the local runtime state.
npm run world:bootstrap prepares a deterministic playable private world. By
default it preserves existing runtime state, reuses compatible resources, and
fails clearly on incompatible state such as a different controller RCL, another
player's room, or a spawn with the requested name in another room. Use
-- --reset only when you explicitly want Screeps Lab to recreate the local
private-server runtime and replace seeded room state with the requested
fixture. Defaults are --room E48N13, --username DaelinSolo, --rcl 1, and
--spawn Spawn1. Optional --spawn-x and --spawn-y request an explicit spawn
tile; otherwise Screeps Lab selects a deterministic valid tile near room center.
Bootstrap artifacts are written to artifacts/world-bootstrap/runs/<run-id>/,
with artifacts/world-bootstrap/latest pointing at the newest run. The summary
records requested and observed room/RCL/spawn/player state, created-vs-reused
resources, reset intent, ticks, diagnostics, and verification result.
npm run sandbox:inspect validates a Screeps Lab snapshot artifact without
mutating the private world. npm run sandbox:import ingests a validated
snapshot into the official private-server runtime and currently requires
--reset, because importing reconstructs room and player state from the
artifact. Additive schema-v1 terrainRooms entries reconstruct terrain needed
by remote routing and road planning; legacy snapshots fall back to the primary
room.terrain. Only the primary room contributes objects, ownership, and
Memory. Auxiliary terrain is published for Game.map.getRoomTerrain without
making those rooms active, visible, owned, or populated. Import summaries list
room names and verification results without embedding full terrain rows. See
Snapshot inspection and import for the contract and
server reconstruction rules. Use --username when the local private-server
account name differs
from the MMO snapshot username, and --steam-id when the game client signs in
through Steam and should land in that imported account. After a successful
import, run npm run play to enter the reconstructed sandbox, or
npm run play -- --creative when you intentionally want local operator
authority over the private world.
npm run snapshot:pull provides the supported one-way WSL-to-OCI artifact
workflow. It discovers the newest remote directory containing snapshot.json,
transfers the complete directory through the operator's existing SSH
configuration, validates it, and only then updates the ignored local
artifacts/incoming/screeps/snapshots/latest link. See
Pulling MMO snapshots from OCI for prerequisites,
configuration, dry-run behavior, and follow-up commands.
npm run subject:smoke also preserves the world by default. It provisions or
reuses a local private-world player, installs a synthetic subject program, runs
one controlled tick, and verifies Memory.screepsLab for that player. With no
credentials configured it creates a generated anonymous test identity. If both
SCREEPS_LAB_SERVER_USERNAME and SCREEPS_LAB_SERVER_PASSWORD are present in
.env, it uses that named local identity. If only one is present, the command
fails before starting the server. The password is never written to artifacts or
game memory.
npm run subject:deploy -- --path ../screeps builds an external local Screeps
repository with an explicit local build command, collects generated .js
modules, installs them into a private-server code branch, advances exactly one
controlled tick, and verifies Memory.screepsLab.externalSubject. It never
uploads to screeps.com and does not run the subject repository's deploy
scripts unless you explicitly configure such a command yourself. The default
build command is npm run build, output directory is dist, entry module is
main, private player is DaelinSolo, and private-server branch is lab.
A normal local end-to-end workflow is:
npm run world:bootstrap -- --room E48N13 --username DaelinSolo --rcl 5 --spawn Spawn1 --reset
npm run subject:deploy -- --path ../screeps
That workflow resets only the private Screeps runtime, creates or reuses the
private player, owns and configures the requested room, creates or reuses the
spawn, installs the external subject, advances controlled ticks, and leaves
evidence under artifacts/world-bootstrap/ and artifacts/subject-deploy/.
Useful deployment options:
npm run subject:deploy -- --path ../screeps --player DaelinSolo --branch lab
npm run subject:deploy -- --path ../screeps --build-command "npm run build" --output dist --entry main
npm run subject:deploy -- --path ../screeps --require-clean
npm run subject:deploy -- --path ../screeps --no-tick
npm run subject:status
Scenario Engine Foundation
npm run scenario:run -- <scenario-id> runs one built-in 0.3.1 representative
scenario. npm run scenario:smoke runs the four representative terminal paths
sequentially against the local private-server stack:
npm run scenario:run -- 0.3.1-pass
npm run scenario:run -- 0.3.1-assertion-fail
npm run scenario:run -- 0.3.1-subject-error
npm run scenario:run -- 0.3.1-timeout
npm run scenario:smoke
Screeps Lab 0.3.2 also provides an external subject scenario runner:
npm run scenario:run -- external-subject-smoke --subject-path ../screeps
npm run scenario:run -- external-subject-smoke --subject-path ../screeps --ticks 100 --timeout-ms 120000
npm run scenario:run -- external-subject-smoke --subject-path ../screeps --branch scenario-external-subject --player DaelinSolo --room E48N13 --rcl 5 --reset
npm run scenario:run -- external-subject-assertion-fail --subject-path ../screeps
npm run scenario:run -- external-subject-subject-error --ticks 5
npm run scenario:run -- mmo-snapshot-smoke --subject-path ../screeps --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --ticks 100 --reset
npm run scenario:run -- mmo-snapshot-smoke --subject-path ../screeps --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --subject-assertions screeps-lab/assertions/mmo-regression.json --ticks 100 --reset
An external subject scenario resolves and validates the external Screeps
repository, prepares a private world, runs the subject build, installs the
complete module map into the requested private-server branch, advances a bounded
number of controlled ticks, observes Memory.screepsLab.externalSubject,
evaluates generic assertions, and writes terminal artifacts. The default world
strategy is deterministic bootstrap. mmo-snapshot-smoke uses the additive
snapshot strategy: it validates a Screeps Lab v1 snapshot artifact during
preparation, requires explicit --reset, reconstructs the private world through
the existing snapshot importer, verifies the import result, then deploys the
external subject to the imported local player. Bootstrap-only options such as
--room, --rcl, and --spawn are rejected for snapshot-backed runs.
Supported generic assertions are world-prepared, subject-executed,
ticks-completed, no-subject-errors, and a read-only memory-path existence
predicate. external-subject-subject-error uses a Lab-owned fixture module map
that records the execution marker and throws intentionally; it does not modify
the real external repository.
Screeps Lab 0.3.4 adds an optional subject-owned assertion manifest. The
scenario subject.assertionManifest property and the CLI
--subject-assertions <path> option resolve a JSON manifest relative to the
external subject checkout. Absolute paths and traversal outside the subject
root are rejected. The manifest is schema-versioned and declarative; it cannot
contain JavaScript, shell commands, callbacks, modules, or arbitrary runtime
expressions. Screeps Lab validates the schema during preparing, records
manifest checksum and Git state, captures Memory before and after controlled
execution, and evaluates only generic Memory path assertions. Colony-specific
meaning stays in the subject repository.
Manifest schema version 1 supports:
{
"schemaVersion": 1,
"id": "mmo-snapshot-regression",
"description": "Basic colony-state regression checks",
"assertions": [
{ "id": "room-memory-exists", "type": "path-exists", "source": "memory", "path": "rooms.E48N13" },
{ "id": "room-status-not-error", "type": "not-equals", "source": "memory", "path": "rooms.E48N13.status", "expected": "error" },
{ "id": "heartbeat-advanced", "type": "increased", "source": "memory", "path": "rooms.E48N13.lastRun" }
]
}
Supported assertion types are path-exists, path-absent, equals,
not-equals, numeric-comparison, allowed-values, collection-size,
changed, unchanged, increased, and decreased. Numeric comparisons and
collection-size assertions require one of >, >=, <, <=, ==, or !=.
Paths are source-relative dot paths; __proto__, prototype, and
constructor segments are rejected, and missing paths remain distinct from
present null values.
Every run writes an isolated artifact directory under
artifacts/scenarios/runs/<run-id>/. artifacts/scenarios/latest points at the
newest single run, and artifacts/scenarios/smoke-latest/ contains the newest
suite index.
External subject scenario artifacts include:
scenario-definition.json
lifecycle-events.json
environment.json
world-preparation.json
subject-provenance.json
snapshot-provenance.json
snapshot-import.json
subject-build.log
subject-deployment.json
observations.jsonl
server.log
assertions.json
subject-assertion-manifest.json
subject-assertion-provenance.json
subject-assertion-results.json
scenario-report.json
cleanup.json
summary.txt
Reports record the scenario ID, run ID, terminal classification, requested and
completed ticks, starting and ending tick, subject path, Git branch and
revision, dirty status, build command and exit code, module count and checksum,
Lab-owned assertion results, subject-owned assertion results and manifest
provenance when configured, lifecycle errors, cleanup status, and artifact
locations.
environment.json is deliberately whitelisted metadata; it does not serialize
the process environment or secret values.
Each scenario report includes separate result fields:
engine.result completed | aborted
scenario.result pass | fail | error | aborted | cancelled
A failed assertion is a completed engine run with scenario.result: "fail".
A captured subject runtime error is a completed engine run with
scenario.result: "error". A bounded execution timeout is an aborted engine run
with an explicit timeout reason.
terminalClassification further distinguishes pass, assertion-failure,
subject-error, timeout, preparation-error, deployment-error,
orchestration-error, and cleanup-error.
Generated artifacts include scenario-definition.json, lifecycle-events.json,
assertions.json, subject-assertion-manifest.json,
subject-assertion-provenance.json, subject-assertion-results.json,
scenario-report.json, summary.txt, and server observation artifacts from
the existing observation writer.
Exit-code semantics:
0 scenario passed, or representative smoke suite matched expected outcomes
1 scenario completed but the scenario result was not pass
2 engine aborted, such as the timeout scenario
3 unexpected engine/internal error
64 CLI usage error
The current scenario contract is deliberately narrow. It supports synthetic representative subjects, external Screeps repository scenarios, deterministic bootstrap world preparation, bounded controlled ticks, a small generic assertion catalog, terminal reports, and cleanup. Rich YAML/JSON scenario DSLs, branching workflows, large fixture libraries, screenshots, replays, telemetry comparison, and distributed workers remain deferred.
Path selection is explicit: CLI --path wins, then a configured subject
definition when one is supplied by callers, then SCREEPS_LAB_SUBJECT_PATH.
Without one of those, deployment fails with setup instructions. Paths are
canonicalized and rejected when missing, not directories, equal to this
repository, nested inside this repository, or when their configured output would
escape the subject checkout.
Interactive Play
npm run play starts or resumes the persistent private world in
.screeps-lab/official-server/ and keeps the server running until stopped. The
world is preserved by default. Only npm run play -- --reset deletes and
recreates the runtime state.
The default tick rate is 1000 milliseconds, which is intentionally
human-playable. Numeric --tick-rate values are minimum start-to-start delays
between controlled ticks. --tick-rate max starts the next tick as soon as the
previous tick has advanced, paused, and persisted. Tick executions are serialized
and never overlap.
When attached to an interactive terminal, play mode supports:
Space Pause or resume
N Advance exactly one tick while paused
+ Increase simulation speed
- Decrease simulation speed
Q Stop cleanly
Ctrl+C Stop cleanly
Speed changes move through these presets:
2000 ms, 1000 ms, 500 ms, 250 ms, 100 ms, 50 ms, max
Use npm run play -- --paused to start with the server paused. Press N to
advance one tick and remain paused afterward. If standard input is not a TTY,
play mode runs non-interactively at the configured rate until SIGINT or
SIGTERM.
Play mode also renders live server observations:
[server] starting
[server] ready
[server] paused
[tick] 23814
[server] running
[stdout] ...
[stderr] ...
These lines are human rendering of structured observation events from the server adapter. They are not an interactive Screeps console prompt; executing arbitrary game-console expressions is handled by the separate Operator Console API.
On startup, play mode prints the actual game and CLI ports returned by the
official launcher. Both interfaces bind to 127.0.0.1. Connect the Screeps
Steam client to the game address. The CLI address is administrative and should
remain private. For remote operation, forward only the game port with a command
like:
ssh -L <local-port>:127.0.0.1:<game-port> <user>@<server>
The official private server uses Steam authentication for normal client login. The subject smoke workflow does not require Steam, a real MMO account, room claiming, spawn placement, or creep creation. It provisions only a local private-world player plus a server-side lab anchor needed for the official runner to execute that player's code.
Creative Mode
Creative Mode is an explicit local operator-authority surface for a running play session. It is disabled by default. Start play with:
npm run play -- --creative
Then type creative help in the play terminal, not the Screeps in-game console,
and press Enter. When you start typing creative..., play mode opens a visible
command> input line. The current implemented commands are:
creative status [--room E48N13]
creative spawn creep --room E48N13 --x 22 --y 14 --name TestHarvester --body work,work,carry,move
creative spawn structure --room E48N13 --x 20 --y 20 --type extension [--energy 50] [--hits 1000]
creative spawn site --room E48N13 --x 21 --y 20 --type extension
creative remove --id <object-id>
creative remove creep --name TestHarvester
creative set energy --id <object-id> --amount 500
creative fill energy --id <object-id>
creative drain energy --id <object-id>
creative set controller --room E48N13 --level 5
creative snapshot --name hand-built-rcl5
Supported direct structure types are spawn, extension, road, container,
storage, tower, and link. Creative Mode validates room names,
coordinates, body parts, terrain walls, duplicate creep names, occupied
positions, supported structure types, energy capacity, and controller level
bounds. Mutations are serialized with controlled ticks; if the world is running,
the play session pauses around the command and then resumes.
Creative Mode is local-only. It targets only the official private server runtime managed by Screeps Lab and does not contain a path to the public Screeps MMO. It does not expose arbitrary JavaScript evaluation, arbitrary database access, terrain editing, bulk clearing, scenarios, assertions, replay, or rewind.
Successful and failed commands are logged as JSON Lines in:
artifacts/play/latest/creative-commands.jsonl
creative snapshot --name <name> records a checkpoint note under
artifacts/play/latest/creative-checkpoints/ and relies on the official private
server's current runtime persistence. It is not an MMO-neutral portable snapshot
contract.
Creative Mode Manual Acceptance
This acceptance test requires the operator to use the Screeps client; automated tests do not perform graphical client verification.
npm run sandbox:import -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --reset --username <local-name> --steam-id <id>
npm run play -- --creative
Connect the Screeps client to the displayed game address. Use the client only to inspect the room or run normal JavaScript console expressions. In the play terminal, optionally press Space to pause, then run Creative Mode commands like:
creative status --room E48N13
creative spawn structure --room E48N13 --x 20 --y 20 --type extension --energy 50
creative spawn creep --room E48N13 --x 22 --y 14 --name TestHarvester --body work,work,carry,move
creative set energy --id <object-id> --amount 25
creative set controller --room E48N13 --level 5
creative remove --id <object-id>
creative snapshot --name hand-built-rcl5
Visibly confirm in the connected client that the structure, creep, energy, and
controller changes appear in the imported room. Stop with Q or Ctrl+C, then
inspect artifacts/play/latest/creative-commands.jsonl.
External Subject Deployment
The external adapter lives in @screeps-lab/subjects and currently supports
local Screeps repositories that build to a directory of CommonJS .js modules.
For the common sibling layout:
~/projects/
├── screeps/
└── screeps-lab/
run:
cd ~/projects/screeps-lab
npm run subject:deploy -- --path ../screeps
The adapter records Git branch, full SHA, short revision, and dirty state when
the subject path is a Git repository. Dirty trees are allowed by default and
appear as <short-sha>-dirty; use --require-clean to reject them.
Before the server is mutated, deployment completes resolution, inspection, build, module collection, validation, wrapper injection, and deterministic manifest generation. Build failures, missing entry modules, invalid module names, source maps, secret-looking files, empty output, or output path escapes stop before private-server code is touched.
The server adapter installs the complete module map as a replacement on the selected private-server branch and activates that branch. Obsolete modules from the previous deployment are removed. If installation fails during the storage write, the adapter attempts to restore the previous branch record. Activation or tick failures are reported with the exact state reached; subject loop failures are surfaced rather than hidden.
Execution verification is Lab-owned and does not require editing the subject
repository. Screeps Lab appends a small wrapper to the configured entry module.
The wrapper writes Memory.screepsLab.externalSubject with the deployment ID,
subject ID, revision, first execution tick, last execution tick, and execution
count, then immediately calls the original exported loop. If the subject loop
throws, the marker records subjectLoopError and the original error is
rethrown. The reserved Memory namespace is
Memory.screepsLab.externalSubject; the reserved module prefix
__screepsLab/ is rejected for subject modules.
Deployment artifacts are written under:
artifacts/subject-deploy/
├── latest -> runs/<run-id>/
└── runs/<run-id>/
├── deployment-summary.json
├── subject-manifest.json
├── module-manifest.json
├── build.stdout.log
├── build.stderr.log
├── startup.log
├── server.log
├── observations.jsonl
├── environment.json
└── console.log
deployment-summary.json is the main proof file. It contains the deployment
ID, subject path, Git provenance, build result, module count and checksum,
player, private-server branch, install/activation state, tick transition,
execution marker, subject-loop error state, and final verification state. It
excludes passwords, tokens, and full module contents.
Imported snapshot workflow:
npm run sandbox:inspect -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json
npm run sandbox:import -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --reset --username DaelinSolo
npm run subject:deploy -- --path ../screeps --player DaelinSolo --branch lab
npm run play -- --paused
npm run subject:status
Creative Mode workflow:
npm run play -- --creative --paused
In the play terminal, prepare the world with Creative Mode commands, then run:
subject redeploy --path ../screeps --player DaelinSolo --branch lab
subject status
Redeploy after changing external subject code with:
npm run subject:deploy -- --path ../screeps
Automated playable room and spawn bootstrap, automatic file watching, Git cloning or pulling, multiple simultaneous subjects, CI integration, and public MMO deployment are not implemented in 0.2.4.
Operator Console API
The Operator Console API exposes controlled access to the private server's Screeps runtime console through the existing server adapter. It is intentionally low level: it submits JavaScript to the official server console queue, advances one controlled tick, captures the returned console payload, and shuts the server down cleanly for one-shot evaluations.
Evaluate a console expression:
npm run lab:eval -- "Game.time"
npm run lab:eval -- "Object.keys(Game.rooms)"
npm run lab:eval -- "Memory"
Open a simple REPL:
npm run lab:console
Objects and arrays from lab:eval are rendered as pretty JSON when possible.
Syntax errors, runtime exceptions, execution timeouts, startup failures, and
transport failures are reported as CLI errors instead of uncaught process
crashes.
Current limitations:
- Console commands run as the dedicated local
ScreepsLabOperatorplayer. - Each
lab:evalstarts the private server, executes one command, then stops it. - This is separate from Creative Mode and does not implement scenario scripting, arbitrary room-construction helpers, automatic spawning, save-file editing, replay editing, or a higher level scripting DSL.
Each operator console run writes evidence under:
artifacts/operator-console/latest/
├── console.log
├── console-summary.json
├── environment.json
├── observations.jsonl
├── server.log
└── startup.log
Current Status
Current status: 0.3.3 snapshot-backed external subject scenarios, 0.3.2 external subject scenarios, 0.2.4 external subject deployment, 0.2.3 Creative Mode, 0.2.2 operator console API, 0.2.1 observation layer, interactive world control, and reset-only snapshot sandbox import are implemented.
Implemented:
- official Screeps private server dependency
- internal server adapter package
- operator CLI smoke command
- operator CLI play command
- first-time runtime initialization
- paused startup and exactly-one-tick advancement
- persistent interactive sessions with pause, resume, manual step, and speed controls
- structured server lifecycle, process-output, and tick observation events
- live play-mode observation rendering
- operator console expression evaluation
- simple operator console REPL
- external local Screeps repository build, install, and execution verification
- explicitly enabled local Creative Mode commands for supported object creation, removal, energy mutation, controller level changes, and private-runtime checkpoints
- observation JSON Lines artifacts
- graceful shutdown of launched server processes
- smoke artifacts in
artifacts/smoke/latest/ - play artifacts in
artifacts/play/latest/ - Creative Mode command artifacts in
artifacts/play/latest/creative-commands.jsonl - subject smoke run artifacts in
artifacts/subject-smoke/runs/, withartifacts/subject-smoke/latestpointing at the newest run - snapshot inspection and reset-only sandbox import from the Lab-owned v1 snapshot contract, including legacy-compatible multi-room terrain catalogs
- snapshot-backed external subject scenarios using reset-only private-world reconstruction
Planned but not implemented:
- 0.3.4 subject-owned assertion contract promotion after successful local deployment validation
- non-reset snapshot import into an existing compatible world
- scenario report indexing and promotion suites
- Overlord agent coordination
- report generation and context export automation
Initial Non-Goals
This repository does not currently:
- deploy code to public Screeps environments
- embed the primary colony repository
- define a production-ready scenario format
- provide a stable API for agents or adapters
- provide a Docker layout or CLI framework beyond the current minimal Node CLI
Smoke Artifacts
Each smoke run writes evidence under:
artifacts/smoke/latest/
├── console.log
├── environment.json
├── observations.jsonl
├── server.log
├── startup.log
└── tick-summary.json
The official server runtime state is stored in ignored local state under
.screeps-lab/official-server/.
Play Artifacts
Each play run writes evidence under:
artifacts/play/latest/
├── console.log
├── creative-commands.jsonl
├── environment.json
├── observations.jsonl
├── server.log
├── session-summary.json
└── startup.log
These artifacts are separate from smoke artifacts and are ignored by Git.
observations.jsonl contains one parseable JSON object per server observation.
server.log is the human-readable rendering of the same observation stream,
including lifecycle transitions, server output, tick numbers, and failure
context. Set SCREEPS_LAB_ARTIFACTS_DIR to place generated artifacts somewhere
other than the repository artifacts/ directory.
session-summary.json separates fatalLines from warning-only
warningLines/diagnosticLines, so backend diagnostics such as isolated
official-server HTTP response races remain visible without automatically ending
an otherwise advancing play session.
Snapshot Sandbox Import
The public MMO bot repository can export a Screeps Lab snapshot JSON artifact. Screeps Lab treats that JSON schema as the integration boundary; it does not import code from the MMO repository.
Inspect an artifact without changing the private world:
npm run sandbox:inspect -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json
Import an artifact into a clean reconstructed world:
npm run sandbox:import -- --snapshot ../screeps/artifacts/screeps-lab-snapshots/<snapshot-id>/snapshot.json --reset --username <local-name> --steam-id <id>
npm run play
The importer validates the v1 envelope, terrain, manifest counts, object refs,
object index, checksum, and raw Memory before mutation. Public MMO object IDs
are treated as provenance and are remapped to private-server object IDs in the
imported player Memory. The imported room is owned by the source snapshot player
unless --username names a different local private-server account. When
--steam-id is present, the importer attaches that Steam identity to the local
owner so browser sign-in resolves to the imported room owner.
Subject Smoke Artifacts
Each subject smoke run writes evidence under a run-specific directory and updates
latest to point at that run:
artifacts/subject-smoke/
├── latest -> runs/<run-id>/
└── runs/
└── <run-id>/
├── console.log
├── environment.json
├── observations.jsonl
├── server.log
├── startup.log
└── subject-summary.json
subject-summary.json records the non-secret identity metadata, subject id,
installation branch, controlled tick result, observed memory marker, and server
diagnostics.
Scenario suites and regression baselines
Screeps Lab 0.3.5 composes registered scenarios into committed suites. Runs are
sequential and each child receives a fresh private-server lifecycle and isolated
scenario artifact directory. The default policy continues after an unsuccessful
child to collect complete evidence; --stop-on-failure stops at the first
unexpected result. Parallel execution is deferred.
npm run suite:list
npm run suite:run -- smoke
npm run suite:run -- smoke --display plain
npm run suite:run -- smoke --display panel --tick-rate 250
npm run suite:run -- smoke --turbo
npm run suite:run -- external-subject-smoke --subject-path ../screeps
npm run suite:run -- snapshot-smoke --snapshot <snapshot.json> --reset
npm run suite:run -- smoke --baseline <baseline.json>
npm run suite:baseline -- smoke --from <suite-report-or-run-id>
suite:run defaults to --display auto: an interactive TTY receives a compact
redrawing status panel, while redirected output, CI, dumb terminals, and captured
Codex output receive stable line-oriented progress. --display panel requests
the panel but falls back clearly when redraw is unsafe; --display plain always
uses line-oriented output. The panel and plain stream show subject preparation,
suite/scenario lifecycle, exact tick progress, observed world tick, elapsed time,
measured throughput, expected-versus-actual outcomes, skips, failures, and the
final artifact directory. The detailed terminal suite summary is still printed.
This is a terminal operator panel, not the deferred web dashboard.
External-subject suites prepare and build the configured repository once, then
reuse the collected module map and suite provenance for every child. Snapshot
suites require an explicit validated snapshot and --reset; generated or
personal snapshots are not committed.
Suite artifacts are written under artifacts/suites/runs/<run-id>/, with
artifacts/suites/latest pointing at the newest run and
artifacts/suites/index.json indexing terminal reports. Each run contains the
normalized definition, provenance, lifecycle events, child report references,
optional baseline comparison, JSON report, text summary, and
progress-events.jsonl. Progress events are small serializable records in
manifest order; they do not duplicate scenario reports, player Memory, complete
environments, credentials, or secret values. Generated runs and
local baselines remain ignored by Git.
Baselines are versioned artifacts created only by the explicit
suite:baseline command from a passing terminal report; normal runs never
replace them. Comparisons detect missing or changed scenarios, terminal and
assertion regressions, fewer completed ticks, new runtime errors, and coarse
wall-clock duration changes. The default timing tolerance is the larger of
1,000 ms or 25 percent. Wall-clock checks are operational signals, not
deterministic performance measurements. Runs with different configured minimum
tick durations retain correctness comparison but omit operational-duration
findings and record a speed-compatibility finding. Overlord orchestration remains future
0.4 work.
Suite simulation speed
Suite speed selection uses discrete server-owned presets: safe (1000 ms),
fast (500 ms), faster (250 ms), turbo (100 ms), and maximum
(the validated adapter minimum, currently 50 ms). --tick-rate accepts the
corresponding millisecond values or max; --turbo selects the fastest
validated preset. These values are minimum official-server cadences, not promised
multipliers. Subject execution, server work, persistence, storage, and the host
can make observed throughput slower.
Exact controlled single ticks still temporarily use a 1000 ms safety floor so
the official server observes the pause boundary without overshoot. Lower presets
provide real acceleration for bounded accelerated execution, while the panel
continues to show measured reality. Interactive [/]/T suite controls
are not exposed in 0.3.5.1 because they would not truthfully accelerate the
controlled path. Compare presets with the short local diagnostic:
npm run speed:benchmark -- --tick-rate 1000 --ticks 20
npm run speed:benchmark -- --turbo --ticks 20
The diagnostic uses a separate ignored private runtime, reports exact completion, elapsed time, observed rate, average tick duration, overlap/overshoot, persistence, cleanup, and fatal/warning output. It never contacts the public MMO.
Accelerated simulations and subject telemetry
Scenario execution defaults to controlled, preserving the pause, one tick,
pause, and persistence sequence. Long private-server runs can explicitly select
bounded acceleration and persistence checkpoints. The adapter stops
free-running execution behind a final-tick guard band, pauses, re-reads
authoritative game time, and advances the remainder with serialized controlled
single ticks. For configured cadences below 1,000 ms, each controlled final tick
temporarily uses a 1,000 ms minimum so the official pause flag takes effect before
another tick can start; the requested cadence is restored while paused after each
tick. Any authoritative overshoot remains a failure. The official
roomsForceUpdate cron remains enabled: it periodically activates inactive rooms
whose scheduled force-update tick is due. Lab only restores its expected
missing-set contract by treating an absent activeRooms value as an empty array
before the backend builds its Loki $nin query. Memory.stats capture reads the
subject's actual player Memory; Lab wall-clock timing is never reported as
Screeps CPU telemetry.
npm run scenario:run -- mmo-snapshot-smoke \
--subject-path ../screeps \
--snapshot artifacts/incoming/screeps/snapshots/latest/snapshot.json \
--ticks 500 --execution-mode accelerated --checkpoint-every 100 \
--telemetry memory-stats --telemetry-every 10 --require-telemetry --reset
Enabled runs stream timeline.jsonl and telemetry.jsonl, then write
simulation-performance.json, telemetry.csv, and
telemetry-summary.json. The CSV contains only finite numeric leaves with
generic escaped paths; JSONL preserves the complete nested telemetry tree and
explicitly records missing Memory.stats. No automatic StatsD, Graphite,
Grafana, or public MMO export occurs.
Suite terminal precedence preserves cleanup, orchestration, deployment, preparation, timeout, subject-error, assertion-failure, and baseline-regression outcomes in that order. Expected-failure contract fixtures count as matched when their actual terminal classification equals the manifest expectation.
Infrastructure and road feature validation
The infrastructure-road-integration suite is the operator-run private-server
gate for the external Screeps 0.3.6.3 feature branch. It enforces the clean
subject branch/version contract, imports an explicitly validated snapshot,
executes ten accelerated scenarios, captures route/infrastructure transitions
and CPU telemetry, and keeps behavioral correctness separate from performance
classification.
The complete suite is deliberately not shortened to fit an agent session. Run it with:
npm run suite:run -- infrastructure-road-integration \
--subject-path ../screeps \
--snapshot artifacts/incoming/screeps/snapshots/latest/snapshot.json \
--server-tick-duration-ms 100 \
--reset
This tick-duration option is strictly opt-in, accepts integer values from 50 through 10000 ms, and requests a minimum cadence. Actual ticks may be slower when simulation work takes longer. Each scenario records the request, completed ticks, simulation duration, effective ticks per second, and checkpoint/persistence overhead in scenario-report.json, simulation-performance.json, and environment.json; suite child references aggregate the same evidence in suite-report.json and child-scenarios.json.
Passing Lab validation does not deploy to the MMO. Candidate qualification and any later beta activation remain distinct, explicitly authorized operations. See the infrastructure and road validation operator guide.
Documentation
License
License information has not been selected yet. See LICENSE.
Public MMO releases
Milestone 0.3.7 adds an operator-controlled, beta-first public MMO release path. It consumes the exact module-map.json preserved by candidate qualification; it never rebuilds the subject or reads its checkout during deployment. Start with an offline plan:
npm run candidate:deploy -- <candidate-id> --target beta --expected-qualified-sha256 <sha256> --dry-run
Real mutations require SCREEPS_TOKEN, an explicit beta or default target, and --authorize-public-mmo. Activation, promotion, and rollback additionally require --authorize-live-activation. No public MMO deployment was performed as part of implementing 0.3.7. See the MMO deployment operator guide.