Skip to content

Architectural Decisions

0001. Use a Monorepo for Screeps Lab and Keep Subjects External

Date: 2026-07-12

Status: Accepted

Context

Screeps Lab needs to coordinate a private Screeps server, orchestration logic, scenario definitions, internal packages, generated artifacts, and subject codebases under test. The first expected subject is often a separate primary Screeps colony repository.

Decision

Screeps Lab will use a single monorepo for lab-owned applications, packages, scenarios, documentation, configuration, scripts, and repository-wide tests. Tested subject repositories will remain external. They must not be committed as nested Git repositories or added as submodules at this time.

Overlord is currently a working title for the orchestration component, not a final product name.

Rationale

A monorepo keeps early architecture simple while the component boundaries are still forming. It allows shared contracts, scenario definitions, and app-level work to evolve together without premature package publishing or cross-repository coordination.

Keeping subjects external preserves ownership boundaries. Screeps Lab can test a bot without becoming the bot repository.

Consequences

  • Lab applications and packages can share internal contracts locally.
  • Subject resolution must support external paths or ignored local workspaces.
  • Reproducibility will eventually require subject version or commit pinning.
  • Documentation must clearly distinguish lab code from colony code.

Alternatives Considered

  • Multiple repositories immediately: deferred because the architecture is too young to justify cross-repository coordination.
  • Nested Git repositories: rejected because they are easy to commit accidentally and blur ownership.
  • Git submodules: deferred because they add workflow complexity before the subject adapter model is defined.

Future Split Conditions

Packages may move to separate repositories later if they develop independent release cycles, external consumers, separate access controls, or substantially different operational ownership.

0002. Wrap the Official Screeps Server Behind an Internal Adapter

Date: 2026-07-12

Status: Accepted

Context

The first implementation milestone needs a real private Screeps world, but Screeps Lab should not spread a specific server implementation throughout the codebase. Future work may use another launcher, Docker layout, storage engine, or custom lifecycle controller.

Decision

Screeps Lab will keep server lifecycle code in packages/server. The 0.1 adapter uses the official screeps npm package and launcher as an implementation detail. The lab CLI depends on the adapter interface, not on official Screeps launcher APIs directly.

The smoke workflow starts the official server paused, observes the current game time through storage, unpauses for one tick, pauses again, waits for the database file to persist the observed tick, and then stops all launched server processes.

Consequences

  • The lab can validate ownership of the private server lifecycle now.
  • Future server implementations can replace the adapter without changing CLI workflow semantics.
  • The official server's Loki storage autosaves on an interval, so smoke runs wait for persistence before shutdown.
  • Local validation requires local port/socket permissions and native build tools.

0003. Screeps Lab Owns the Simulation Clock

Date: 2026-07-12

Status: Accepted

Context

Milestone 0.2 needs an operator to keep a private Screeps world running and play with it interactively, but the lab still needs deterministic tick control for future scenarios and regression checks. Allowing the official server to run freely would make pause boundaries, persistence, and artifact interpretation less predictable.

Decision

Screeps Lab will own the simulation clock by repeatedly performing serialized, persisted, single-tick advances. A controlled tick pauses the world, observes the current tick, resumes the official server, waits for exactly one tick advance, pauses again, waits for persistence, and only then allows another tick to start.

Continuous play mode is a serialized asynchronous loop over that primitive. The configured tick rate is a minimum start-to-start interval, and max means there is no extra delay after the previous tick has completed and persisted.

Consequences

  • Interactive play and smoke validation share the same safety model.
  • Ticks cannot overlap or queue behind a timer.
  • Manual stepping leaves the world paused after exactly one tick.
  • High speeds may be slower than configured if the server needs more time to execute or persist a tick.
  • The official server remains behind the packages/server adapter boundary.

0004. Keep Subject Configuration Generic and Server Provisioning Adapter-Owned

Date: 2026-07-12

Status: Accepted

Context

Screeps Lab needs a subject foundation before it can run scenarios or external Screeps repositories. The first useful validation is intentionally small: create or reuse a private-world player, install synthetic code, advance a controlled tick, and observe a deterministic memory marker.

The official private server stores players, code branches, and memory in its own storage layer. Its runner also requires a user to have at least one room object before that user's code is executed.

Decision

Subject configuration lives in @screeps-lab/subjects and is independent of the official server. The initial subject type is synthetic-smoke, which writes a namespaced Memory.screepsLab marker and performs no room claiming, spawn placement, creep creation, or subject-driven world mutation.

Official private-server player provisioning, code installation, the minimal runner anchor, and memory observation live behind the @screeps-lab/server adapter. The anchor is a server-side temp room object marked as Screeps Lab infrastructure so the official runner executes the player, while the game object builder skips it.

Configured identities require both SCREEPS_LAB_SERVER_USERNAME and SCREEPS_LAB_SERVER_PASSWORD. The password is used only to detect intentional configured identity selection; it is not printed, serialized, stored in run artifacts, or written to Screeps memory.

Consequences

  • The lab can prove subject execution without a Steam account or public MMO workflow.
  • Future external subject adapters can replace only subject resolution and module preparation.
  • Official-server storage details remain inside the server adapter.
  • Automated room ownership and spawn bootstrap remain future work.

0005. Treat Screeps Lab Snapshots as the MMO Integration Boundary

Date: 2026-07-13

Status: Accepted

Context

The public Screeps MMO bot repository exports owned-room state for local experimentation. Screeps Lab needs to ingest those exports without taking a dependency on the MMO repository's TypeScript modules, workspace layout, or runtime implementation.

Decision

Screeps Lab owns a compatible v1 snapshot contract in @screeps-lab/snapshots. That package parses snapshot JSON artifacts, validates envelope, terrain, manifest, object, checksum, and raw Memory invariants, builds a neutral import plan, and rewrites raw Memory references from public MMO object IDs to private-server object IDs.

Official private-server mutation remains in @screeps-lab/server. The initial operator import workflow requires --reset for all imports and reconstructs a clean private world from the artifact. sandbox:inspect provides a non-mutating validation path.

Consequences

  • Screeps Lab and the MMO bot repository remain independently owned.
  • Snapshot JSON is the integration boundary.
  • The first importer is conservative: it rejects unsupported v1 object shapes and never overwrites an existing world without explicit --reset.
  • Future work can add provably safe compatible re-imports without changing the boundary.

Amendment: additive multi-room terrain catalogs (2026-07-18)

Schema v1 now accepts optional top-level terrainRooms. The snapshot package validates unique Screeps room names, rows-v1, fixed 50×50 dimensions, and supported terrain cells, requires the primary room exactly once, and sorts the effective catalog in neutral import plans. Legacy artifacts without the field normalize to one terrain entry from room.name and room.terrain; checksum verification remains unchanged.

The official-server adapter writes and verifies every effective terrain record and the runtime terrain publication before subject execution. Primary-room objects, ownership, ID remapping, and Memory retain their existing behavior. Auxiliary entries are terrain-only and do not imply visibility, active-room membership, ownership, controllers, sources, minerals, structures, sites, or Memory. This amendment extends the data carried across the existing snapshot boundary; it does not move official storage shapes into the snapshot package or change the schema version.

0006. Expose Normalized Server Observation Events

Date: 2026-07-13

Status: Accepted

Context

Screeps Lab 0.2 begins the observation and interaction phase. The lab needs live operator feedback, artifact capture, and testable waits for server behavior while keeping future consumers such as Overlord independent from the official Screeps launcher, child-process streams, and storage internals.

Decision

@screeps-lab/server exposes a scoped observation stream on each server adapter instance. The stream emits serializable lifecycle, output, and tick events. Consumers subscribe to those normalized events or wait for predicates such as a later tick. CLI rendering and artifact persistence are separate consumers of the same stream.

Rationale

Normalized events give tests, artifacts, and operators one stable contract. The adapter can internally combine child-process stdout/stderr, lifecycle evidence, and storage polling without leaking process handles or official-server implementation details. Tick waits can use the same authoritative game-time source as controlled ticking while presenting an event-oriented API.

Consequences

  • Consumers no longer need to parse child-process output directly.
  • Observation artifacts can be JSON Lines plus a readable log for the same run.
  • Subscriber failures are contained so one consumer does not prevent another from receiving events.
  • The official launcher details remain replaceable behind the adapter.
  • Some tick events are synthesized through adapter-owned polling until a better native event source exists.

Alternatives Considered

  • Direct stdout consumption in each CLI command: rejected because it would duplicate parsing and tie consumers to child-process details.
  • Parsing log files after the run: rejected because live operator feedback and tick waiting need timely observations.
  • External event-bus dependency: deferred because 0.2.1 only needs scoped in-process subscribers.
  • Waiting for a hypothetical native server event API: rejected because the lab already has an authoritative storage tick source and needs observation now.

0007. Make Creative Mode Typed Local Operator Authority

Date: 2026-07-14

Status: Accepted

Context

Screeps Lab needs an operator to take explicit control of an imported private world and visibly prove that the local sandbox can be changed through supported tools. The project already has a low-level operator console API, but arbitrary JavaScript evaluation or direct database access would blur safety boundaries and would not establish the higher-level operations future scenario tooling should reuse.

Decision

Creative Mode is an explicitly enabled play-session capability, started with npm run play -- --creative. It exposes typed commands for supported local mutations: creating creeps, selected structures, construction sites, explicitly removing objects, changing energy stores, changing controller level, reporting status, and recording a private-runtime checkpoint note.

The CLI parses and logs operator commands, but the official private-server adapter owns validation, object construction, player ownership, terrain and collision checks, storage writes, runtime restart publication, and persistence readback.

Consequences

  • Operators can prove authority in a connected private client without editing database files.
  • Creative Mode cannot target the public Screeps MMO.
  • The first slice remains intentionally narrow and avoids a scenario language, arbitrary evaluator, database shell, terrain editor, bulk clearing, replay, or rewind.
  • Future scenarios can reuse adapter-owned operations instead of duplicating raw official-server storage shapes.

0008. Deploy External Subjects Through Lab-Owned Preparation and Verification

Date: 2026-07-15

Status: Accepted

Context

Screeps Lab needs to run the real external colony repository in a local private server without turning that repository into a submodule, modifying it for lab integration, or exposing any path to the public Screeps MMO. A successful deployment must prove execution, not merely copy code into storage.

Decision

External subject preparation belongs in @screeps-lab/subjects. The first adapter, external-screeps-repository, resolves and validates a local path, runs an explicit local build command, collects generated Screeps modules, records Git provenance, injects a Lab-owned execution wrapper, and returns a normalized prepared subject.

Private-server mutation belongs in @screeps-lab/server. The server adapter provisions the local player, replaces the complete private-server code branch, activates it, advances one serialized controlled tick, and observes Memory.screepsLab.externalSubject.

The wrapper is appended to the configured entry module rather than moving the entry module to a reserved name. This preserves relative CommonJS require(...) behavior in compiled Screeps output.

Consequences

  • Screeps Lab can deploy a sibling checkout such as ../screeps without committing or modifying that checkout.
  • Build behavior remains explicit and local-only; public MMO deployment scripts are not selected implicitly.
  • Execution proof is generic and does not require subject repository changes.
  • Subject loop errors are visible as deployment evidence instead of being hidden by a successful wrapper marker.
  • The server adapter remains the only layer that knows official private-server code storage details.

0009. Bootstrap Owned Private Worlds Through the Server Adapter

Date: 2026-07-16

Status: Accepted

Context

After external subject deployment, Screeps Lab could build, install, execute, and verify a real external subject, but it still depended on manually prepared private-world room state. Milestone 0.2 needed a deterministic way to create a playable owned room without coupling the lab to one bot's internals or relying on fragile ad hoc database edits from the CLI.

Decision

World bootstrap is exposed as npm run world:bootstrap. The CLI owns argument parsing, operator rendering, and run artifacts. The @screeps-lab/server adapter owns official-private-server lifecycle, storage mutation, controlled tick advancement, persistence waiting, and authoritative re-read verification.

Bootstrap is non-destructive by default. Compatible existing player, room, controller, terrain, and spawn state is reused. Incompatible state fails with a clear operator error. Destructive behavior requires --reset, which recreates the private-server runtime through the existing lifecycle and clears seeded room state before creating the requested fixture.

Spawn placement is deterministic. Operators may request explicit coordinates; otherwise the adapter chooses a valid tile near room center. Placement rejects walls, room edges, occupied tiles, and bootstrap anchor conflicts.

Consequences

  • External subjects can be tested against a repeatable owned room and spawn.
  • The scenario engine can reuse a Lab-owned world fixture service instead of inventing storage mutation logic.
  • Private-server storage shapes remain behind the server adapter boundary.
  • --reset remains the only destructive bootstrap flag.
  • The initial fixture is intentionally narrow: one owned room with controller, spawn, two sources, one mineral, and metadata sufficient for normal subject execution.

0010. Separate Scenario Engine Results from Scenario Outcomes

Date: 2026-07-16

Status: Accepted

Context

Screeps Lab 0.3 introduces scenarios as repeatable experiments over a private server, subject deployment, bounded execution, observations, assertions, and reports. A failed experiment should not automatically mean that Screeps Lab failed to execute the experiment. Conversely, a subject runtime error should be captured as evidence when the engine can still report it.

Decision

Scenario execution lives in @screeps-lab/scenarios. The package defines typed engine results (completed, aborted), scenario results (pass, fail, error, aborted, cancelled), lifecycle phases, report creation, assertion evaluation, and representative 0.3.1 scenarios.

The engine always attempts to produce a terminal report, even after preparation, deployment, execution, observation, evaluation, reporting, or cleanup failures. The report records lifecycle events, phase outcomes, ticks, assertions, deployment identity, captured errors, artifact paths, and cleanup status.

The 0.3.1 representative suite uses synthetic scenario subjects by default. This keeps the foundation deterministic while still exercising the real local private-server lifecycle, code installation, controlled tick execution, memory observation, artifact writing, and cleanup through the existing adapters.

Consequences

  • An assertion failure is engine.result: completed and scenario.result: fail.
  • A captured subject runtime error is normally engine.result: completed and scenario.result: error.
  • A bounded timeout is engine.result: aborted with an explicit terminal scenario result and reason.
  • Future richer DSLs and external-subject scenario definitions can build on the contract without changing result semantics.
  • The first suite is intentionally narrow and contract-focused; broad fixture libraries, branching workflows, screenshots, replay systems, telemetry comparison, and distributed workers remain deferred.

0011. Compose External Subject Scenarios Through Adapter Boundaries

Date: 2026-07-16

Status: Accepted

Context

Screeps Lab 0.3.2 needs to run a real external Screeps repository through a full scenario workflow: validate the subject, prepare a private world, build and deploy modules, advance bounded controlled ticks, observe execution, evaluate assertions, report artifacts, and clean up. The lab already has world bootstrap, external subject deployment, server control, and observation capabilities.

Decision

External subject scenarios are defined in @screeps-lab/scenarios as typed JavaScript objects created by createExternalSubjectScenario. The scenario engine owns lifecycle ordering, terminal classification, generic assertions, and run artifacts. It does not reimplement world bootstrap, subject builds, module collection, code installation, tick control, or memory observation.

World preparation is a scenario boundary with strategy: "bootstrap" in 0.3.2. The engine invokes the server adapter's bootstrap service during the deployment phase before subject installation. This keeps snapshot-backed strategies available for later work without changing the lifecycle.

External subject preparation remains behind the external-screeps-repository adapter. The adapter resolves the external path, runs the configured build, collects modules, injects the Lab-owned execution marker, and returns Git and module provenance. The scenario engine serializes that provenance and asks the server adapter to install and activate the complete module map.

Consequences

  • Screeps Lab can run external subjects without importing or committing their source code.
  • The scenario package composes existing services and stays independent of official-server storage details and subject repository layouts.
  • Generic assertions are limited to subject execution, exact tick completion, no uncaught subject errors, and read-only Memory path existence.
  • Terminal reports distinguish pass, assertion failure, subject error, timeout, preparation error, deployment error, orchestration error, and cleanup error.
  • Colony-specific behavioral assertions remain subject-owned or future work.
  • Snapshot-backed scenario execution can reuse the world strategy boundary later.

0012. Reuse Reset-Only Snapshot Import for Snapshot-Backed Scenarios

Date: 2026-07-16

Status: Accepted

Context

Screeps Lab 0.3.3 needs to test a candidate external Screeps revision against a private reconstruction of MMO-derived colony state. The repository already owns a v1 snapshot package and reset-only official-server importer.

Decision

Snapshot-backed scenarios are an additive world.strategy: "snapshot" branch inside createExternalSubjectScenario and the existing scenario engine. The scenario package validates the artifact with @screeps-lab/snapshots during preparation, builds an import plan, and asks the server adapter to import that plan during deployment. Snapshot scenarios require explicit reset authorization and reject bootstrap-only room/RCL/spawn options.

The server importer remains the only implementation of official-server storage mutation, object ID remapping, Memory rewriting, persistence waiting, and import verification. After import, the engine installs the external subject on the imported local player returned by the world preparation result.

Consequences

  • Snapshot and subject provenance are captured in the same terminal report.
  • Snapshot validation failures classify as preparation failures; import failures classify as deployment failures.
  • Bootstrap scenarios continue to use the existing deterministic world setup.
  • Non-reset compatible imports, colony-specific assertions, promotion suites, automatic MMO deployment, and branch promotion remain deferred.

0013. Keep Subject Assertions Declarative and Subject-Owned

Date: 2026-07-17

Status: Accepted

Context

Screeps Lab 0.3.3 can reconstruct a private world, deploy an external Screeps repository, run bounded controlled ticks, detect subject runtime errors, and evaluate Lab-owned generic smoke assertions. Those assertions prove that the Lab experiment executed, but they do not define colony-specific correctness.

The external subject repository is the right owner for behavior expectations such as room state, economy health, operation status, logistics readiness, or heartbeat paths. Screeps Lab must remain independent of any one colony bot and must not learn colony-specific Memory schemas, creep roles, spawn policy, remote operations, or infrastructure plans.

Decision

External subject scenarios may name a subject-relative JSON assertion manifest. The manifest is versioned, schema-validated, declarative, and loaded from the same subject checkout that is built and deployed. Screeps Lab records manifest provenance, including resolved and subject-relative path, checksum, byte size, assertion count, validation result, subject Git revision and branch, dirty-tree state, and tracked/modified/untracked manifest status when Git can report it.

Screeps Lab owns generic evaluation and safe observation. The initial catalog supports only bounded source: "memory" path assertions: path-exists, path-absent, equals, not-equals, numeric-comparison, allowed-values, collection-size, changed, unchanged, increased, and decreased. Numeric operators are an explicit finite set. Paths are relative to the Memory root, unsafe prototype-pollution segments are rejected, and missing paths remain distinct from present null values.

The manifest cannot contain JavaScript, shell commands, callbacks, modules, runtime expressions, or arbitrary Game evaluation strings. Screeps Lab captures before and after Memory observations through the existing server adapter boundary and reports Lab-owned and subject-owned assertion results separately.

Consequences

  • Subject repositories can define colony-specific expectations without coupling Screeps Lab to that colony's implementation details.
  • Manifest validation failures classify as preparation failures before private-server mutation when practical.
  • Failed subject-owned assertions classify as assertion failures, not engine crashes.
  • Dirty, modified, or untracked local manifests are allowed for explicit local testing but are visible in provenance and summaries.
  • Arbitrary executable assertions, general remote Game expressions, telemetry-baseline comparison, exact live-creep continuity assertions, and automatic promotion or MMO deployment remain deferred.

0014. Compose Suites Above the Scenario Engine and Use Explicit Baselines

Date: 2026-07-17

Status: Accepted

Context

Regression runs need multiple isolated scenarios to share one reproducibly prepared external subject while retaining the scenario engine's lifecycle, world preparation, deployment, execution, observation, assertion, reporting, and cleanup behavior. A previous-run convention would make accepted behavior ambiguous and could silently bless failures.

Decision

@screeps-lab/suites is an orchestration layer above @screeps-lab/scenarios. It validates committed JSON manifests, resolves scenario references in manifest order, prepares an external subject once when configured, injects that immutable prepared module map into each child, and invokes the existing engine sequentially. Every child still owns a fresh server lifecycle and artifact directory. Snapshot world preparation uses the existing validated snapshot import plan and server adapter.

Baselines use an explicit versioned JSON contract and may only be written by an operator command from a passing terminal report. Normal suite execution is read-only with respect to baselines. Comparisons emit structured findings and use configurable timing tolerances; duration is treated only as a coarse operational signal.

Terminal aggregation prioritizes cleanup, orchestration, deployment, preparation, timeout, subject runtime error, and assertion failure. A regression classification applies after current outcomes match their declared expectations. Contract fixtures whose expected non-pass classification matches the observed result do not fail the suite.

Consequences

  • External subjects are built once per suite rather than once per scenario.
  • Suite reports reference child reports instead of duplicating their evidence.
  • The local atomic JSON index is deterministic and repairable without a database.
  • Suite execution is sequential in 0.3.5; parallel workers are deferred.
  • Exact subject checksum equality is provenance, not a regression requirement.
  • CPU, richer telemetry, CI, dashboards, and Overlord behavior remain deferred.

0015. Qualify Immutable Build Artifacts Separately from Deployment

Date: 2026-07-17

Status: Accepted

Context

A Git commit alone does not identify the exact JavaScript module bytes tested by a local regression run. Rebuilding during a future deployment could produce different bytes, and a dirty or drifting checkout makes qualification untrustworthy.

Decision

@screeps-lab/releases validates a clean external checkout against an explicit resolved revision, prepares it once, passes that same prepared module object to all suite children, and preserves it as module-map.json. Qualification uses an explicitly supplied, checksum-validated snapshot and explicit private-world reset permission. Git state is captured before the build and after the suite.

Candidate attempts are versioned, isolated artifacts classified as eligible, rejected, or error. Eligibility describes policy over the suite and provenance; engine completion remains a separate field. SHA-256 protects the canonical module artifacts, while snapshot provenance records the snapshot's existing declared checksum algorithm. Inspection is read-only.

Consequences

  • A future deployment workflow can consume and verify exact module bytes without rebuilding.
  • Assertion failures, runtime errors, timeouts, and regressions reject a tested candidate; preparation, integrity, drift, orchestration, and cleanup failures are errors.
  • Candidate artifacts are generated and ignored, never committed by default.
  • MMO upload, deployment, promotion, rollback, and token handling remain out of scope.

0016. Separate Subject-Build Identity from Qualified-Artifact Identity

Date: 2026-07-18

Status: Accepted

Context

The external-subject adapter adds a Lab-owned execution wrapper containing a unique deployment ID to the entry module. Two qualifications of the same clean, deterministically built revision therefore preserve different final bytes even though the direct subject build is identical.

Decision

Preparation computes subject-build module-map and module-manifest SHA-256 checksums immediately after collection and before wrapper injection. It then injects the wrapper and retains the existing qualified module map as the exact tested artifact. Candidate manifests distinguish subjectBuild from qualifiedArtifact; the latter includes the deployment ID. The uninstrumented map is not duplicated, but its aggregate identity and per-module manifest are stored in subject-build-manifest.json.

Build-once means one subject build within a qualification attempt. Every attempt creates a distinct immutable candidate. Immutability means a candidate's bytes cannot change after qualification; it does not imply that separately instrumented candidates are byte-for-byte reproducible.

Consequences

  • Subject-build checksums are the stable comparison surface for deterministic builds of the same source revision.
  • Qualified-artifact checksums identify one attempt's exact tested and future deployable bytes and may differ across attempts.
  • A future deployment must verify and consume the preserved qualified module-map.json without rebuilding, normalizing, or reinstrumenting it.
  • The additive schema-version-1 fields preserve inspection compatibility with older candidates, which report subject-build identity as not recorded.

0017. Qualified Artifacts Are the Public Deployment Source

Date: 2026-07-18

Status: Accepted

Decision

Public MMO upload and promotion consume artifacts/candidates/runs/<candidate-id>/module-map.json after eligibility and integrity validation. Deployment never rebuilds, reinstruments, or rereads generated code from a subject checkout. Promotion revalidates and uploads that same candidate artifact instead of copying code from beta.

Rationale

Qualification establishes an immutable release input. Exact remote readback provides byte-level proof, while execution markers provide separate behavioral evidence. Keeping transport in @screeps-lab/mmo and policy/evidence in @screeps-lab/releases preserves subject and private-server independence.

Consequences

Legacy candidates without subject-build and qualified deployment identity are readable but not deployable. Every mutation captures prior remote bytes first, and public/live authorization gates remain explicit.

0018. Bounded Acceleration and Artifact-First Subject Telemetry

Date: 2026-07-18

Status: Accepted

Decision

Controlled persisted-per-tick execution remains the default. Accelerated execution is an explicit scenario policy that advances bounded chunks inside one private-server lifecycle, observes authoritative game time at every chunk boundary and persists at configured checkpoints. Before the final target it uses a fixed guard band, pauses, re-reads authoritative time, and serializes the remaining controlled single ticks. An observed overshoot is always a failure; the exact final tick is persisted. Lab wall-clock performance and Screeps subject telemetry remain separate report domains. Fast configured cadences are temporarily raised to a 1,000 ms minimum only for each controlled final tick, then restored while paused, preventing the official loop from starting a second tick before it observes the pause flag.

The official roomsForceUpdate cron remains active. It periodically selects non-border rooms absent from the transient active-room set and activates rooms whose scheduled force-update tick is due. The Loki storage adapter returns undefined for a missing set even though the backend expects empty-set semantics; JSON-RPC then drops the undefined $nin operand and Loki receives an empty predicate. Lab normalizes only that missing value to [] and records sanitized cron, RPC, query-operator, storage-error, tick, and timestamp diagnostics. It does not suppress cron failures or backend exits.

The private-server adapter reads the player's authoritative Memory. Scenarios treat the actual JSON-compatible Memory.stats tree as the only generic subject telemetry boundary and stream it to run-local artifacts. Capture does not alter subject modules, qualified candidate module maps, or public MMO branches, and it does not export to the MMO Graphite/Grafana pipeline. A future external sink must use an isolated namespace such as screeps.lab.<run-id>.* while preserving Game.time as the simulation index.

Consequences

  • Persistence waits no longer scale one-for-one with accelerated ticks, but exact final tick and final persistence remain mandatory.
  • Sampling frequency is explicit because pausing to read Memory can affect measured throughput.
  • JSONL preserves non-numeric telemetry; CSV and numeric summaries remain colony-agnostic.

0019. Keep Feature Validation in Scenario Adapters with Independent CPU Policy

Date: 2026-07-18

Status: Accepted

Context

Infrastructure and road integration needs subject-aware behavioral evidence, but generic server, suite, and release packages must remain reusable. The v1 snapshot contract reconstructs objects for one owned primary room and may reconstruct terrain-only auxiliary rooms, but cannot reproduce every remote-world condition or auxiliary gameplay object.

Decision

Lab registers a committed ten-scenario suite and keeps route/infrastructure interpretation in a scenario evidence adapter. Generic private-server mutation gains only a typed, hits-bounded setHits operation; snapshot source IDs are resolved through the importer mapping before mutation. Unavailable remote and incidental-traffic mutations are represented as explicit contract-level limitations rather than overstated live-world proof.

Scenario reports preserve focused evidence, while suite reports expose behavioral classification and CPU classification independently. No performance threshold or accepted baseline is fabricated. Candidate policy rejects an unaccepted meaningful CPU regression even when behavioral assertions pass.

Consequences

  • The external Screeps repository remains read-only and external to Lab.
  • Generic server and release packages do not import subject modules or encode colony route shapes.
  • A passing suite can still recommend a rerun when CPU evidence is inconclusive.
  • The long tick-driven suite remains intact and is delegated to the operator under the 15-minute agent validation policy.
  • Lab validation does not deploy, activate, promote, or authorize a public MMO branch.

0020. Opt-In Official-Server Minimum Tick Duration

Date: 2026-07-18

Status: Accepted

Decision

Scenario and suite execution may explicitly request a bounded private-server minimum tick duration. The CLI accepts integer values from 50 through 10000 ms and leaves the request unset when omitted. The official-server adapter owns the implementation: on every startup and reset it generates a runtime-local mod using config.engine.mainLoopMinDuration and seeds the matching supported persisted tickRate; omission applies the official 1000 ms default.

Screeps Lab continues to seed paused startup and owns serialized pause, resume, authoritative boundary verification, checkpoint persistence, final persistence, and overshoot detection. The requested value is a minimum cadence rather than an achieved-rate claim. Scenario and suite artifacts preserve requested duration, completed ticks, simulation wall time, effective observed ticks per second, and persistence overhead.

Consequences

  • No installed package or external subject repository is patched.
  • Runtime-local generated configuration remains ignored and is recreated deterministically.
  • Processing time may impose a practical minimum slower than the requested duration.
  • The ten-scenario, 3,300-tick infrastructure suite and its validation policy remain unchanged.

0021. Node and Screeps Driver Runtime-Snapshot Compatibility

Date: 2026-07-18

Status: Accepted

Context

Screeps Lab supports the official private-server stack on Node 22. The driver runtime at node_modules/@screeps/driver/build/runtime.snapshot.bin is a generated V8 heap snapshot tied to the exact V8 runtime that created it. A binary produced by Node 24 or another incompatible V8 can make the official runner abort with SIGTRAP. Rebuilding native addons does not regenerate this artifact. Despite the shared word "snapshot," it is unrelated to MMO JSON world snapshots.

Decision

Lab standardizes official private-server scenario and suite execution on the Node 22 range declared by .nvmrc and package.json. Scenario and suite CLI paths perform one shared preflight before private-server startup. The preflight loads the exact driver runtime snapshot into isolated-vm in a subprocess, so an incompatible snapshot can terminate only the probe rather than the Lab CLI. Only failed, unreadable, corrupt, incompatible, or missing artifacts are regenerated, and a regenerated artifact must pass the same probe before execution continues. Reports preserve concise Node, V8, path, initial result, regeneration, and final result metadata.

Operators retain explicit verification and recovery commands:

npm run driver-snapshot:verify
npm run driver-snapshot:prepare

Application code does not invoke nvm and does not switch runtimes automatically.

Consequences

  • Compatible artifacts add one bounded child-process probe and are not rebuilt.
  • Driver snapshot preparation may update the generated binary in node_modules, which remains uncommitted dependency state.
  • A failed regeneration or final verification stops execution with an actionable environment error before the private server reaches an opaque SIGTRAP.
  • MMO JSON world snapshots are never modified, refreshed, or invalidated in response to driver V8 compatibility failures.
  • Compatibility failures are environment failures, not subject-code errors or

0022. Observe Suite Progress Without Coupling Orchestration to Terminals

Date: 2026-07-18

Status: Accepted

Decision

Suite and scenario runners accept optional best-effort observers for versioned, plain serializable progress events. Scenario lifecycle and tick progress originate inside the engine; the suite adds run and manifest-entry identity, persists the ordered JSONL stream, and forwards it to consumers. Observer and presentation failures cannot change terminal classification. The CLI separately reduces and renders events as a TTY panel or plain lines.

Simulation speed has three separate meanings: the scenario engine owns exact game tick targets, the server adapter owns the official server's configured minimum tick duration, and the panel reports observed wall-clock throughput. Discrete presets map to supported adapter durations. Controlled single ticks keep the 1000 ms pause-boundary safety floor; faster configured durations are effective in bounded accelerated execution and are never presented as guaranteed multipliers. Timing baselines made under different configured minima omit duration regressions while retaining correctness comparison.

Consequences

  • Suites remain usable without observers and do not depend on terminal APIs.
  • The CLI does not poll partial reports or parse human-readable logs.
  • Interactive throttle keys are deferred until they can change effective speed at safe boundaries for the active execution mode.
  • The panel is not a web dashboard, and suite execution remains sequential.

0023. Scenario-Owned Scheduled World Mutations

Date: 2026-07-19

Status: Accepted

Decision

Snapshot-backed scenarios may register bounded world mutations at their normal telemetry/evidence boundaries. The scenario engine invokes the hook only after persisted Memory observation, records its result in the scenario timeline, and preserves the final mutation record in structured artifacts. Scenario modules remain responsible for semantic target selection, typed private-server commands, phase timing, and assertions.

The hook does not expose subject source imports, replace engine semantics, or permit public MMO writes. Logistics performance scenarios sample and mutate on the subject's existing 25-tick rollup cadence.

Consequences

  • Multi-phase blockage, recovery, invalidation, and decay contracts use the normal scenario engine instead of bespoke execution loops.
  • Creative mutations retain official private-server persistence checks and snapshot import mappings.
  • Collision timing and long eligibility/decay windows remain operator-run where deterministic completion exceeds the agent validation budget.
  • Final mutation artifacts include scheduled, applied, failed, and limited actions, while timeline.jsonl retains their execution order.