Skip to content

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++-10 or 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 ScreepsLabOperator player.
  • Each lab:eval starts 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/, with artifacts/subject-smoke/latest pointing 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.