Debugging
This is the canonical reference for debugging tools, console helpers, inspect commands, status values, and planned diagnostics in this Screeps bot.
See ../ROADMAP.md for milestone-level debugging and observability plans.
See LIVE_API.md for live Screeps API research, console/WebSocket behavior,
and the recommended Codex integration architecture.
When adding a new console helper, global command, inspect function, profiler, visualization helper, or debugging utility, update this file in the same pull request as the implementation. Operator-facing debug surfaces drift quickly; keep statuses, intent meanings, console examples, and inspection workflows updated whenever the code changes.
Available Debug Helpers
Debug globals are registered by registerDebugGlobals() in src/debug/index.ts
when src/main.ts loads.
Available globals:
inspect(name)debug.help(topic?)debug.enable()debug.disable()debug.status()debug.history(limit?)debug.colonies()debug.colony(roomName)debug.creep(name)debug.room(roomName?)debug.cia(roomName?)debug.dossier(roomName)debug.assessment(roomName)debug.ciaEvents(roomName?)debug.spawn(roomName?)debug.spawnPlan(roomName?, role?)debug.spawnOverrides(roomName?)debug.boostedWorkforce(roomName?)debug.setSpawnCount(role, count, duration?, roomName?)debug.setBoostedCount(role, count, duration?, roomName?)debug.clearSpawnCount(role, roomName?)debug.clearSpawnCounts(roomName?)debug.clearBoostedCount(role, roomName?)debug.body(roomName?)debug.gov()debug.approvedLeases()debug.approveLease(workClass, duration?)debug.clearApprovedLease(workClass)debug.economy(roomName?)debug.mineral(roomName?)debug.factory(roomName?)debug.factoryStart(product, amount, roomName?)debug.factoryCancel(roomName?)debug.lab(roomName?)debug.labStart(product, amount, roomName?)debug.labCancel(roomName?)debug.marketQuotes(resource, direction, roomName?)debug.marketBuy(resource, amount, maximumUnitPrice?, roomName?)debug.marketSell(resource, amount, minimumUnitPrice, roomName?)debug.marketDeal(roomName?)debug.marketCancel(roomName?)debug.marketBuyLimit(resource, maximumUnitPrice, minimumCreditReserve?)debug.marketBuyLimits()debug.marketAutonomy(mode?)debug.clearMarketBuyLimit(resource)debug.links(roomName?)debug.logistics(roomName?)debug.logisticsNetwork(roomName?)debug.logisticsNodes(roomName?)debug.logisticsRoutes(roomName?)debug.logisticsRoute(routeId, roomName?)debug.remote(homeRoomName?)debug.remoteOperations(homeRoomName?)debug.remoteOperation(operationIdOrTargetRoomName, homeRoomName?)debug.externalOperations(homeRoomName?)debug.externalOperation(operationIdOrTargetRoomName, homeRoomName?)debug.externalAllocation(homeRoomName?)debug.externalPortfolio(homeRoomName?)debug.externalAuthority(kind?, mode?)debug.externalControl(action, objectiveId, homeRoomName?)debug.deposits(homeRoomName?)debug.depositOperation(homeRoomName?)debug.depositAuthority(mode?)debug.approveDepositOperation(opportunityId)debug.remoteRoads(homeRoomName, targetRoomName?)debug.colonize(targetRoomName, bootstrapperRoomName)debug.colonization(targetRoomName)debug.cancelColonization(targetRoomName)debug.buildOrders()debug.buildOrder(id)debug.pending(roomName?)debug.infrastructureAuthority(mode?)debug.show(id)debug.approve(id)debug.reject(id, reason?)debug.cancel(id, reason?)debug.approveRemoteRoad(id)debug.labCluster(roomName?)debug.reserveLabCluster(roomName, candidateId)debug.releaseLabCluster(roomName)debug.cancelBuildOrder(id, reason?)debug.remoteConstruction(roomName?)debug.clearAllFlags()debug.infrastructurePlan(roomName?, mode?)debug.colonyLayout(roomName?, visualize?)debug.reconsiderPlan(id, reason?)debug.explainPlan(id)debug.terminal(roomName?)debug.maintenance(roomName?)debug.intents()debug.towers()debug.beginLabSnapshot(roomName)debug.labSnapshotStatus()debug.cancelLabSnapshot()debug.clearLabSnapshot()
All helpers return strings so they can be called directly from the Screeps console.
Infrastructure Authority
Normal infrastructure construction authority is a persistent operator policy:
debug.infrastructureAuthority()
debug.infrastructureAuthority("autonomous")
debug.infrastructureAuthority("manual")
autonomous is the default. It authorizes authoritative desired local
construction directly and releases retained remote/defense build orders without
creating a local operator ticket. It does not bypass placement: the ordinary
execution pass still revalidates ownership, RCL and
structure limits, construction-site limits, exact coordinates, tile legality,
anchors, topology, existing structures/sites, and remote-operation authority.
Changing manual to autonomous releases existing pending normal work in
place; it does not create replacement plans, orders, or packages. Stale work is
therefore still rejected by execution revalidation. Changing back to manual
affects future proposals; work already authorized continues normally.
Remote-road routes remain explicitly manual in both modes. Use
debug.pending(), debug.show(id), and debug.approve(id) for those routes and
for normal infrastructure while manual authority is active. debug.pending()
reports the current authority mode so an empty normal-infrastructure queue is
not mistaken for missing planning.
Migrated fixed local structures and local Roads no longer execute through build orders or packages. In manual mode, inspect and approve one exact desired item:
debug.localConstruction("E48N13")
debug.localConstruction("E48N13", "local-road-intent:E48N13:20:21", "approve")
debug.localConstruction("E48N13", "local-road-intent:E48N13:20:21", "cancel")
The approval stores an exact fingerprint only. It does not copy geometry or track construction lifecycle. Fixed-local orders and packages are not consulted.
debug.mineral(roomName?)
Displays the persisted local mineral-operation state plus direct lookups of its assigned mineral, extractor, container, and miner. Output includes mineral type and amount, regeneration ticks, extractor cooldown, mineral/container capacity, desired miner count, CPU population mode, economy eligibility, and the concise policy reason. When native Storage allocation is active, it also reports density, target/stored/remaining inventory, protected native capacity, actual Storage energy and free capacity, effective energy capacity/fill, regeneration refresh tick, route state, transferable amount, assigned hauler, and the current capacity or policy reason. A separate assignment line explains whether the route is inactive, physically blocked, waiting for inventory or a protected-service hauler, eligible for bounded assignment, or already assigned. It does not rerun population or mineral policy.
debug.mineral()
debug.mineral("E48N13")
The implemented route ends at primary Storage and authorizes only the exact
adjacent operation container and native resource. The command does not imply
Terminal staging, terminal sends, market behavior, industry, remote minerals,
or arbitrary-resource hauling.
Spawn-count overrides may pause the miner with 0, but cannot exceed one or
bypass infrastructure, economy, workforce-recovery, or CPU safety gates.
Operator-Directed Factory Production
Factory production begins only through an explicit operator command:
debug.factory()
debug.factoryStart("utrium_bar", 1000)
debug.factoryStart("utrium_bar", 1000, "E48N13")
debug.factoryCancel()
debug.factoryStart(product, amount, roomName?) requires a visible owned room,
an owned completed Factory, a positive finite amount, and a recipe compatible
with the Factory's current level. Recipe output is cycle-sized: a request that
is not an exact multiple of the recipe output is normalized upward to the
smallest whole number of cycles, and the returned message states both the
requested and normalized target. A second order is rejected while production
or cancellation cleanup is active.
The room-scoped Factory workflow stages at most the deficits for one recipe
cycle from primary Storage. It supports every resource type accepted by the
recipe, including energy. One ordinary local hauler at most receives semantic
factory-supply or factory-output work; urgent spawn, extension, and tower
service remains higher priority, and the final local-service hauler is never
assigned optional Factory work. Terminal sourcing, inter-room imports,
prerequisite production, market activity, recipe recommendations, and
autonomous product selection are not part of this workflow.
debug.factory(roomName?) reports Factory id, level, cooldown, relevant store
contents, product, requested and normalized targets, successfully produced and
remaining amounts, output per cycle, components, staged and missing next-cycle
inputs, lifecycle state, blocker, assigned hauler, last produce() result, and
CPU/economy eligibility. Common states are:
staging: one cycle's available input deficits are moving from Storage.waiting-materials: one or more required resources are absent from Storage.waiting-cooldown: the full next cycle is staged but the Factory is cooling.paused-cpu: recovery or emergency bucket governance has paused new staging and production without deleting the order.paused-economy: the existing optional-spending policy is closed.paused-factory: a levelled recipe needs a matching activePWR_OPERATE_FACTORYeffect.evacuating: finished product is moving to primary Storage.complete: the normalized target was produced through successfulOKresults and its remaining Factory output was evacuated.
debug.factoryCancel(roomName?) immediately prevents further produce()
calls. Existing Factory assignments finish safely; staged recipe inputs,
finished product, and tagged carried cargo are returned to primary Storage.
Starting a replacement order waits until this bounded cleanup is finished.
Cancellation does not delete the compact final operation summary, so
debug.factory() can explain what happened.
Operator-Directed UO/LH Chemistry and Market Deals
The bounded reaction pipeline accepts UO and LH orders:
debug.labStart("UO", 1000, "E48N13")
debug.labStart("LH", 360, "E48N13")
debug.lab("E48N13")
debug.labCancel("E48N13")
The start command requires owned Storage and at least three mutually compatible Labs. It assigns two stable reagent Labs and every compatible output Lab, normalizes output to five-unit reaction increments, and counts only successful reaction intents toward the requested batch. Ordinary haulers stage exact remaining reagent deficits and bank product when an output reaches 500 units, fills, or the order finishes. UO consumes U/O; LH consumes L/H. One industry transfer job may be active per hauler, urgent energy work preempts new collection, and the final local-service hauler remains protected. While authorized work is active, the population plan temporarily maintains a second local hauler for bounded industry transfers; CPU or economy emergency and active defense suppress that additional demand. Cancellation stops reactions and evacuates assigned minerals.
For the configured first hub, an active E48N13 order derives its missing O from live Storage, Terminal, and reagent-Lab inventory. E48N14 stages at most that shortfall and sends bounded 1,000-unit Terminal transfers while preserving its Terminal energy target. Authorized chemistry and Market work stages energy from Storage up to the target plus the currently calculable transaction cost while preserving the room's minimum operating reserve. This is not general room balancing or arbitrary resource distribution. Dynamic Lab optimization is deferred; assignment IDs remain stable for the order and cannot change until cleanup empties them.
Immediate Market deals use existing counterparties rather than persistent exchange orders:
debug.marketQuotes("utrium_bar", "sell", "E48N13")
debug.marketSell("utrium_bar", 1000, 0.8, "E48N13")
debug.marketBuyLimit("LH", 1.25, 10000)
debug.marketBuy("LH", 1000, undefined, "E48N13")
debug.marketDeal("E48N13")
debug.marketCancel("E48N13")
Sales stage only the unsold amount. Purchases require either an explicit maximum
price or a stored buy limit; stored limits also preserve the configured credit
reserve. Deals are capped at 1,000 units per execution, preserve Terminal target
energy, and refresh a missing/invalid counterparty no more often than every 25
ticks. Purchased resources return to Storage. Explicit operator directives may
run while optional-work recovery or reopening admission is closed. CPU governor
emergency and economy emergency pause reactions, material staging, mineral
authorization, Terminal sends, and Market execution without deleting directives.
No persistent Market orders,
repricing, or speculative purchasing are included. Narrow autonomous selling is
documented in docs/MARKET_AUTONOMY.md; it applies only to regenerated native
mineral surplus and reuses these immediate-deal controls.
Inspect the current surplus evaluation or switch persistent authority:
debug.marketAutonomy()
debug.marketAutonomy("manual")
debug.marketAutonomy("autonomous")
The inspection explains native-target eligibility, protected and committed
inventory, executable buy-order prices, normalized path values, the selected
path and gross advantage, processing-premium acceptance, safety gates, and the
active Factory/Lab/Market action. It also reports the plan's last material
progress tick/age and distinguishes preempted boost handoff from stale
no-progress cancellation. Both terminal states wait for normal current-state
reevaluation; neither resumes the old price or resource assumption. Manual mode
retains recommendations without starting new autonomous work.
Operator-Directed Boosted Workforce
Patch 2 adds sibling workforce intent without changing final-count override semantics:
debug.setBoostedCount("builder", 2, 5000, "E48N14")
debug.setBoostedCount("harvester", 1, "forever", "E48N13")
debug.boostedWorkforce()
debug.boostedWorkforce("E48N14")
debug.clearBoostedCount("builder", "E48N14")
Builders are additive: two requested boosted builders are maintained in addition to automatic builder demand. Harvester counts select source-service slots instead; a count cannot exceed the target's visible sources and does not add permanent harvesters. A boosted harvester is committed only when its ordinary source worker enters a conservative replacement window. Uncommitted preparation reserves capacity only while the source remains covered. Once the boosted creep starts spawning, its reservation survives incumbent loss for a bounded spawn, boost, and travel window so an ordinary replacement cannot shoot the handoff gap. A missing or overdue committed creep restores ordinary local replacement authority.
The operator names only role, count, duration, and target. The configured capability list currently selects E48N13, maps builders to LH and harvesters to UO. Builders use the ordinary selected body; UO harvesters use three boosted WORK parts, which harvest 12 energy per tick and saturate a normal source without the two excess WORK parts in the ordinary five-WORK body. Missing UO reuses the bounded Patch 1 reaction order when U and O are ready; otherwise the corresponding stored Market buy limit is required. Missing LH similarly starts a bounded local reaction when enough L and H are already available, falling back to its stored direct-LH buy limit otherwise. A committed boost-preparation slot preempts an active surplus-liquidation plan, whose owner cancels only subordinate liquidation-authored Market, Lab, or Factory work. Explicit operator directives remain authoritative, so provisioning waits with an explainable blocker rather than replacing operator work. Preempted liquidation waits for the ordinary market-autonomy cadence to reevaluate current state and does not resume its old sale automatically.
One compact slot at a time is prepared for spawning/boosting. Ordinary protected
haulers clear and fill its assigned output/boost Lab with the exact compound and
energy required by the selected body's boostable WORK parts. Compound staging
prefers Storage and falls back directly to Terminal inventory, so replacing a
completed Market directive cannot strand purchased boost material. A fresh
creep keeps its ordinary builder or harvester role while temporary lifecycle
memory moves it through spawn, boost, deployment, and target ownership handoff.
That temporary memory and its provider slot are removed after handoff.
If delayed provisioning lets an ordinary safety replacement take the originally reserved source, the boosted harvester rechecks the visible target at handoff. It prefers an uncovered source, then the source with the oldest ordinary incumbent, while excluding sources already boosted or reserved by another slot.
Final spawn-count overrides and boosted intent for the same room/role are
mutually exclusive and rejected in either order. Expiration or clearing removes
only uncommitted preparation; already spawning, boosting, or deploying creeps
finish and surviving boosted workers are never killed or recalled. CPU governor
emergency, emergency CPU austerity, economy emergency, missing essential
provider workforce, and unmet defense demand pause new provisioning. Recovery
or reopening closure alone does not. cpu.profile.sections.boostedWorkforce
isolates the global lifecycle cost; broad creep discovery is skipped when no
provider slots exist, and new preparation discovery is cadence-bounded to five
ticks.
The infrastructure planner remains authoritative for the existing
factory-core need. Factory commands never place construction sites. If no
Factory exists, the start/status command explains that condition.
debug.help() is the discoverability source for this surface. It groups
commands by approvals, spawning, infrastructure, remoteOps, logistics,
cpu, rooms, intelligence, colonization, snapshots, and general, and
marks every command [read] or [write]. Use a category or command name for
more detail:
debug.help()
debug.help("remoteOps")
debug.help("approve")
debug.help("setSpawnCount")
The help registry is static metadata. Calling it does not execute commands, scan rooms, recalculate plans, or perform fuzzy business-data searches.
Operator Console
The preferred approval workflow is:
debug.pending()
debug.show(17)
debug.approve(17)
Numeric approval tickets
Every new approval-requiring retained build-order or remote-road revision receives a process-wide monotonically increasing numeric ticket. It is displayed as eight zero-padded decimal digits. Padding is display-only, and exact canonical string IDs remain valid.
A numeric ticket stores only lifecycle kind, room, canonical ID, exact revision, and a fixed-width fingerprint hash. Before dispatch, the operator layer re-reads authoritative Memory and rejects stale revisions or changed topology. References are bounded, closed tickets never reopen, and pending build orders still expire through normal reconciliation.
Migrated local construction does not create a generic ticket. In manual mode it
uses the exact fingerprint surface documented under
debug.localConstruction(roomName, itemId, action).
Generic approval commands
Use:
debug.pending()
debug.pending("E48N13")
debug.show(17)
debug.show("remoteRoad:E48N13:E48N14:<sourceId>")
debug.approve(17)
debug.reject(17)
debug.reject(17, "poor extension position")
debug.cancel(17)
debug.cancel(17, "operation retired")
debug.pending(roomName?) scans only compact visible or remembered owned-room
infrastructure Memory when invoked, sorts current tickets numerically, and
returns a bounded readable list. A visibly lost room is excluded and its
retained pending references are closed on inspection; explicit remembered
non-ownership continues to win after vision disappears instead of reviving a
stale colony marker. Each entry shows the numeric and canonical IDs, kind,
room, revision, purpose or target,
coordinate or road counts, concise state, and a copyable action. An ordinary
actionable proposal shows debug.approve(number); expired, blocked, or stale
entries show only valid close actions or explain why no numeric action is safe.
If the 100-reference store is full, a legacy pending target remains visible as
#unassigned with an allocation warning and exact canonical recovery actions
instead of disappearing from the list.
debug.show(id) accepts a numeric or exact canonical ID and prints the ticket
and current revision/fingerprint validity, lifecycle state, target or bounded
coordinates, proposal reason, blockers, approval state, and only the actions
that are valid for that lifecycle. It does not dump unrelated room Memory.
Generic commands dispatch to the retained authoritative build-order or remote-road approval operation. Build orders support rejection and cancellation according to their lifecycle rules. Remote roads currently support approval only; generic reject or cancel reports that the operation is unsupported and does not invent a new state. Remote-road approval also revalidates that the stable backing operation still exists and is active or suspended, including when routine road planning was skipped; a retired operation cannot be approved through either a numeric or canonical command. Empty-path routes and routes carrying the planner's blocked fingerprint also fail closed in both the generic resolver and the authoritative manager.
The retained specialized aliases are:
debug.approveRemoteRoad(id)
debug.cancelBuildOrder(id, reason?)
Package-specific approval commands were removed with the package lifecycle.
All generic and specialized mutating approval entry points use the same exact resolver and record one operator-history entry.
Final spawn-count overrides
Spawn-count overrides are deliberately final operator authority. The live decision order is:
automatic economy/logistics demand
RCL and mature-population caps
CPU population caps
hard colony safety floors
operator override
effective desired count
No later code restores a safety floor after the override. Setting harvesters or haulers to zero is permitted and can strand a colony:
debug.spawnPlan()
debug.spawnPlan("E48N13")
debug.spawnPlan("E48N13", "builder")
debug.setSpawnCount("builder", 2)
debug.setSpawnCount("builder", 2, 5000)
debug.setSpawnCount("upgrader", 0, "forever")
debug.setSpawnCount("hauler", 4, 1000, "E48N13")
debug.spawnOverrides()
debug.spawnOverrides("E48N13")
debug.clearSpawnCount("builder")
debug.clearSpawnCount("builder", "E48N13")
debug.clearSpawnCounts()
debug.clearSpawnCounts("E48N13")
builder and harvester overrides collide with sibling boosted-workforce
intent because one is a final count and the other needs additive/source-slot
semantics. Clear one before setting the other.
Bucket CPU governor
Use debug.gov() for a compact view of the persisted CPU and Memory resource
signals plus Launch Control policy:
debug.gov()
debug.help("gov")
debug.help("cpu")
The CPU output includes bucket posture, live bucket, completed-tick utilization EWMA and source, advisory CPU grade, commanded tier, state age, next transition threshold, numeric reopening capacity, capacity bucket trend, autonomous and effective targets, operator override, active lease count, admission cap, and peak-relative bucket loss, plus the persisted reason. It reads the existing snapshot and does not recalculate or mutate the governor.
The appended Memory Governor block reports the last measured incoming
serialized RawMemory length, normalized sample growth, growth EWMA, pressure
state, evaluation age, last reclamation tick, last reclaimed-item count, and
cumulative run/item counters. It reads Memory.memoryGovernor without calling
RawMemory.get(), reevaluating pressure, or invoking cleanup. healthy,
elevated, and critical are independent of CPU posture and Launch Control
leases.
The bucket governor is the sole producer of the tier consumed by managers. Its config-driven bands are:
open: remain above 5,000; normal autonomous reopening completion commands the operating tierrecovery: command at leastconstrained; remain until 9,000emergency: enter at 1,500; commandcriticaluntil 3,000, then recover
Within open posture, capacity zero commands constrained, capacity one commands
healthy, and reaching the normal autonomous proven ceiling (currently four)
commands abundant. Capacity above four during an operator experiment does not
change the governor tier or the cadence of unrelated CPU-throttled work.
Launch Control combines proven numeric capacity with the current target instead
of relying on a separate managed-descent posture.
Returning to open from recovery does not release all optional work at once.
Reopening capacity remains zero for 100 ticks, permits one optional-work lease
for a 150-tick observation window, and then exposes each subsequent capacity
level only after another 150-tick observation when the charged-CPU EWMA is no
higher than 20 CPU per tick. Recovery-cadence periodic work remains
deterministically staggered until normal autonomous reopening reaches capacity
four. Losing more than 250 bucket points from the latest peak reached at a
capacity level backs the ramp down exactly one level. This gives a single
100-tick Launch Control lease
enough room to complete inside the 150-tick observation window while retaining
a bounded response to sustained descent. The peak-relative reference detects
renewed descent even when the capacity level began at a much lower bucket value.
Harvesters, haulers, defenders, spawning, towers, and other colony-safety work
remain continuous.
debug.gov() reports both the normalized governor EWMA and the comparable
estimated engine charge. Calculated observations display the four-CPU
observability offset explicitly, avoiding the appearance that a draining bucket
is below the 20-CPU limit. The reopening lines report capacity age, change from
capacity entry, loss from the capacity peak, normal/effective/operator targets,
active leases, and the current admission cap.
The same policy owns a small optional-work admission state shown by
debug.gov(). Every 50 ticks it classifies the useful runway as brief,
moderate, or extended from the current bucket, distance to the 5,000
recovery boundary, and the existing smoothed charged-CPU trend. The class is
deliberately coarse and remains unchanged between reviews.
Launch Control grants at most one lease for a moderate horizon and six for an
extended horizon. Extended admission uses one urgency-driven priority lane and
rotation lanes favoring never-admitted and longest-waiting eligible work.
Subsystems submit a compact request with urgency and reason; classes without
current work do not consume a lease. Mineral development uses a cheap,
read-only current-state eligibility probe so a stale zero-demand operation can
request reevaluation; the admitted mineral policy remains the only owner of
operation state and population demand. Local logistics and
infrastructure share the highest optional baseline, while scouting,
intelligence, and mineralDevelopment sit above low-priority
remoteDevelopment. In a one-slot moderate horizon, an applicant waiting 100
eligible ticks outranks a recurring same-priority winner. Eligible wait accrues
only while the class is requested, affordable, unleased, and the governor is
open.
The operator may prioritize a requesting class for one of those existing slots:
debug.approveLease("remoteDevelopment", 1000)
debug.approveLease("remoteDevelopment", "forever")
debug.approvedLeases()
debug.clearApprovedLease("remoteDevelopment")
This approval changes lease selection only. It does not create a work request,
increase maximumActiveLeases, change the governor or commanded tier, reopen
Launch Control, relax the work horizon, or bypass emergency safety. An approved
class therefore remains pending without current demand and remains waiting when
normal lease capacity is zero. When selected, it receives its configured normal
lease duration, consumes one ordinary slot, and is shown with the operator
lease lane. Remaining slots use the normal priority/rotation selection.
Existing active leases are preserved before new selection. If too many approved
requesting classes compete for the remaining slots, Launch Control chooses the
least recently admitted approved class; a tie uses the fixed CpuWorkClass
order shown by cpuWorkClasses. This makes repeated approvals fair and fully
deterministic without adding operator-configurable priorities.
The operator may separately replace the global lease-count target without certifying higher capacity immediately:
debug.setLeaseCap(5, 1000)
debug.setLeaseCap(5, "forever")
debug.setLeaseCap(1, 500)
debug.clearLeaseCap()
debug.gov()
Targets are bounded from zero through cpuWorkClasses.length (currently six).
An upward override advances one capacity level per normal observation window;
it does not jump directly to the target. A lower override clamps new admissions
but preserves already proven normal capacity and lets excess active leases
expire naturally. Clearing or expiration restores the horizon-derived
autonomous target without revoking leases or discarding proven capacity within
the six-class autonomous ceiling. The override changes no work-class
minimum horizon, request freshness, lease duration, selection priority,
fairness, governor state, emergency/recovery behavior, or operator-approved
work-class preference.
scouting, intelligence, logistics, and infrastructure require a
moderate horizon. mineralDevelopment and remoteDevelopment require an
extended horizon. Leases last 100, 50, 100, 100, 250, and 500 ticks
respectively. Requests expire after 100 ticks unless the owning subsystem
renews them. An applicant below its minimum horizon is deferred to the next
50-tick review. Full lease capacity leaves waiting applicants ready for the next
vacancy instead of phase-delaying them. Recovery clears deferrals because its
zero-capacity horizon cannot grant work. A lease continues through ordinary
bucket movement, but emergency posture overrides it.
Remote development no longer implies scouting. Stale or missing adjacent-room
evidence requests scouting; a newly observed dossier requests matching CIA
intelligence; only a current observation/assessment pair permits
remoteDevelopment to apply. This preserves the dependency order without
letting a remote lease manufacture its own prerequisite capability.
The bounded state is Memory.cpuPopulationPolicy.workAdmission plus the
adjacent reopening and optional operatorApprovedLeases records:
{
workAdmission: {
horizon,
horizonReviewedAt,
classes: {
remoteDevelopment: { admittedUntil?, deferredUntil?, lastAdmittedAt? },
scouting: { admittedUntil?, deferredUntil?, lastAdmittedAt? },
intelligence: { admittedUntil?, deferredUntil?, lastAdmittedAt? },
logistics: { admittedUntil?, deferredUntil?, lastAdmittedAt? },
infrastructure: { admittedUntil?, deferredUntil?, lastAdmittedAt? },
mineralDevelopment: { admittedUntil?, deferredUntil?, lastAdmittedAt? }
}
},
reopening: {
capacity, // bounded 0..cpuWorkClasses.length
capacityEnteredAt,
capacityStartedBucket,
capacityPeakBucket
},
operatorLeaseCap: {
target,
createdAt,
expiresAt?, // omitted only when indefinite=true
indefinite
},
operatorApprovedLeases: {
remoteDevelopment: {
createdAt,
expiresAt?, // omitted only when indefinite=true
indefinite
}
}
}
Each class may additionally contain bounded requestedAt, urgency,
requestReason, leaseLane, and eligibleWaitTicks fields. The six class keys
are fixed; there is no queue or history. Inspect them directly when validating
a deployment:
debug.gov()
JSON.stringify(Memory.cpuPopulationPolicy.workAdmission)
Object.keys(Memory.cpuPopulationPolicy.workAdmission.classes)
debug.gov() prints work admission as one indented line per class so request
reasons, operator approval, effective lease lane, and lease timing remain
readable in the Screeps console. debug.approvedLeases() gives the compact
management view with current request and lease state plus the clear command.
Expired approvals and deferral markers are removed during the normal admission
update instead of appearing as deferred(0 ticks) after their request has
ended.
Launch Control enhancement leases
debug.gov() appends the separate six-owner enhancement pool. It shows
capacity, active count, each owner's baseline/enhanced state,
base-lease validity, current cheap request with urgency and reason, remaining
lease time, exact baseline-to-enhanced budgets, the last ending reason, and
cumulative lifecycle counters. This is a read-only projection of the current
Resource Governor and enhancement records; it does not run admission, planning,
pathfinding, logistics evaluation, or resource measurement.
Base leases grant optional-work permission. A binary enhancement lease only raises bounded work quality for that already-admitted owner. It never grants independent domain authority. Capacity is zero under CPU recovery/emergency, a brief or constrained CPU state, or critical Memory; moderate abundance provides one slot and abundant extended-horizon conditions provide two. Elevated Memory still permits bounded compute and fixed-size conclusions; critical Memory prevents enhancement.
Enhancement leases last 25 ticks and require owner demand no more than one tick
old. Base-lease loss, stale demand, or zero capacity ends a lease promptly.
Ranking reuses base class priority, urgency, bounded eligible wait,
least-recent grant, and fixed class order. Useful demand is not manufactured to
fill the pool, so capacity=2 with active=1 is healthy behavior. The retained
Memory is six fixed current-state records, one optional fixed-size Mineral
planning conclusion, and compact grant/revocation counters—not a history.
Infrastructure requests enhancement only when dirty domains or pending local
route paths exceed its baseline evaluation bounds. Its dirty-domain budget is
2 -> 4 and route-path-calculation budget is 1 -> 2; approval, revision,
legality, site placement, economy, and safety gates do not change. Logistics
requests only from persisted material route evidence after the network is at
least 10 ticks old but before its normal 25-tick evaluation is due. An enhanced
early evaluation remains on the existing five-tick room stagger and never turns
the network into every-tick discovery.
Intelligence requests enhancement when at least two early periodic reassessments, or one strategically relevant reassessment, wait between the 25-tick enhanced threshold and ordinary 50-tick refresh. The extra pass inspects at most 64 dossiers and updates at most four; no richer persistent dossier resolution is added. Scouting requests only when at least two legal colony-frontier targets wait. Enhanced selection compares at most four of those targets by current-room distance; route distance, candidate count, traversal, waypoint, avoidance, and population bounds are unchanged.
Remote Development requests only when at least two existing non-inactive candidates can be reconsidered between 25 and the ordinary 50 ticks. Enhanced mode changes only that evaluation interval; the active-room and workforce caps and every CIA, Economy, Defense, Logistics, Infrastructure, Spawn, and lifecycle gate remain unchanged. Mineral Development requests rarely: an RCL6+ room with missing completed mineral prerequisites must already have changed existing Extractor/Container planning evidence. It inspects at most 32 entries, summarizes at most four, and cannot propose, approve, place, spawn, or change inventory authority.
Admission gates optional discovery, planning, and population demand rather than
colony safety. The first owned-room observation and live hostile ingestion can
bypass the CIA lease. Local harvesters, one required local-service hauler, the
minimum controller upgrader, defense, spawning, towers, and safe reconciliation
remain protected. Ordinary builders require infrastructure; one active
critical-defense repair builder and colony-bootstrap pioneers are explicit
exceptions. Mineral policy and miner demand require mineralDevelopment; its
read-only wake-up probe can only submit the request for that admission.
Additional local haulers require logistics plus a persistent route window
showing deficit, low idle capacity, a usable destination, sufficient confidence,
and ongoing production or consumption. Two new network evaluations at least 25
ticks apart must preserve that evidence before one additional count is
committed. The colony grants at most one optional commitment every 150 ticks,
choosing the highest-urgency mature candidate after a one-tick arbitration
delay. debug.logisticsNetwork(roomName) exposes candidate evaluations,
readiness, committed count, and the last authorization. Existing optional
haulers may finish their lives after the logistics lease expires, but the spawn
target returns to the protected floor until a logistics lease is active again.
A fresh evaluation that withdraws sustained need removes the stored commitment
above the protected floor.
Workforce recovery compares against the same one-hauler floor so denied optional
capacity cannot keep the room in recovery.
Downward restrictions are immediate. Reaching 9,000 from recovery enters open
posture but retains the recovery tier, then relaxes one adjacent tier every
configured 100-tick reopeningStepDelayTicks.
Recovery and emergency posture also constrain already-alive creep execution, not only future population. Optional strategic managers, scouts, minerals, bootstrap planning, snapshot publication, dashboards, and logistics evaluation pause. Remote and bootstrap creeps run at reduced staggered cadences; local builders and upgraders are staggered; and full stats collection runs every two ticks in recovery and every five in emergency. Local harvesters, haulers, spawning, towers, links, economy assessment, and controller work stay eligible.
The CPU spike flight recorder keeps exactly the latest tick whose final
Game.cpu.getUsed() sample reached the configured 45 CPU threshold. It is
independent of the normal stats cadence and stores the already-collected global,
per-room, role, and hauler-phase CPU profile under Memory.cpuSpike, together
with the bucket, governor/tier context, and incoming serialized RawMemory string
length. The threshold is configured by cpuSpikeFlightRecorderThreshold in
src/config/cpu.ts. RawMemory is read only after a spike qualifies, so this
extra context adds no work to ordinary ticks. Inspect the latest snapshot with:
JSON.stringify(Memory.cpuSpike, null, 2)
The snapshot is replaced only by a later qualifying spike; it is not a history
and is intentionally outside Memory.stats so normal telemetry does not ingest
its forensic dimensions.
The CPU observer still uses the prior completed tick's utilization EWMA. Its
newest input is engine-charged CPU derived from bucket movement, including
post-loop costs such as memory serialization. When either sample is empty or
full, it falls back to the late prior-tick Game.cpu.getUsed() sample. The
shared 4 CPU cpuObservabilityOffset applies only to movement-derived samples.
The observed grade can tighten recovery from constrained to critical, but
cannot command managers independently. Effective observed thresholds are 18,
19.5, and 21 CPU for abundant, healthy, and constrained; higher is
critical.
cpuPopulationUtilizationSmoothingFactor controls EWMA response; the default
0.05 is approximately a 39-tick effective window. The tier caps retain their
existing behavior: constrained and critical cap optional population, while
critical also suppresses remote-harvester replacement. Essential floors and
final operator spawn-count overrides remain authoritative. Memory keeps only
compact current state and observation values, not a transition history.
The default duration is 1,000 ticks. A numeric duration must be a positive
finite integer. Zero never means indefinite; an indefinite override requires
the explicit string "forever". Counts must be non-negative integers, roles
must be valid CreepRole values, and rooms must be visibly owned or carry
remembered owned-colony evidence. If a visible room is no longer owned, visible
state wins and the command is rejected.
Temporary entries store the room, role, requested count, creation tick, and
expiration tick. Indefinite entries carry an explicit marker. Expiration is
effective immediately when Game.time >= expiresAt; the role returns to its
automated count and the small expired entry is removed during that room's next
population-plan or override inspection.
debug.clearSpawnCounts() clears every stored override map under
Memory.rooms, including a stale override for a room whose ownership was later
lost. Passing a room name limits the clear to that room. Explicit clear commands
may remove an existing stored override after ownership loss, but setters remain
restricted to visible or remembered owned rooms.
debug.spawnPlan() reuses the live population and body-selection logic on
invocation. For every requested role it shows current effective workforce,
automatic demand, RCL result, CPU result, safety-floor result, active operator
override and expiration, final effective count and reason, plus selected body
name, cost, size, WORK parts, and CARRY parts. Diagnostic body-scaling Memory is
cloned so inspection does not update ordinary body-policy state.
The diagnostic calculation uses the same workforce-recovery suppression flag as
the live room pipeline, so recovery-mode counts and body choices do not diverge
from spawning.
Logistics-driven hauler counts deliberately leave fractional ordinary carry
demand unfilled. Routes with no movable energy and no current destination
demand do not contribute to spawning, and recent logistics idle-capacity
evidence can hold the current count. Full estimated coverage is reserved for
actionable urgent or critical routes with at least 75 ticks of performance
evidence, low destination blockage, and no more than 10% network idle capacity.
These inputs refresh with the existing cadence-bounded logistics evaluation;
debug.logisticsNetwork(roomName) exposes the idle ratio and route evidence
used by the policy. Shared-source and shared-destination alternatives contribute
only the limiting endpoint workload, rather than multiplying one source's
production or one destination's demand. If economy recovery must replace the
protected final hauler, body policy approves the stable tier when affordable;
the minimal body remains the immediate low-energy fallback.
A successful setter response warns when the request is below a recorded safety floor or above an active RCL/CPU cap. The warning is informational; the request is stored and obeyed. Colony autonomy can still report a zero-essential-worker room as blocked or unrecoverable, but that status does not silently change the operator count. For a remembered room that is not visible, or one without a current economy assessment, the setter instead warns that a live floor/cap comparison is unavailable.
Bounded command history
Use:
debug.history()
debug.history(10)
History is newest-first and strictly limited to 8 compact entries. It records successful and failed generic/specialized approval mutations, spawn override set/clear actions, the room and ticket or role/count context, and a concise outcome. It never stores full command output or Memory objects, and its free-text context fields are length-bounded before persistence. A requested display limit is clamped to the configured bound.
All ticket resolution, help formatting, pending/show formatting, warning plan
calculation, and history formatting occurs only when an operator invokes a
command. Routine tick cost is limited to approval allocation when a genuinely
new revision is created and up to eight direct room-map lookups while finalizing
a population plan. This feature adds no pathfinding, telemetry, broad routine
Game scans, unbounded history, or duplicate normal population-plan execution.
If the bounded ticket store cannot accept another live proposal, that room's
new approval planning is deferred on the existing infrastructure blocked-retry
cooldown without replacing the prior authoritative plan or route and
without aborting room execution. A successful approval, rejection,
cancellation, or automatic terminal lifecycle transition clears the relevant
deferred gate because it may have released closed-reference capacity; ordinary
observation and dirty-domain changes remain the other invalidation sources.
Allocation and deferred-proposal cost stays in the room CPU profile's
infrastructure section, while the eight role lookups remain inside the
existing spawnPlanning section.
Colony Autonomy
debug.colonies() prints one compact line per visible owned room with its
autonomy state, local workforce, effective harvester and hauler counts, idle
spawn availability, local recovery capability, and inter-colony assistance
state.
debug.colony(roomName) expands one room with current energy, the 200-energy
recovery boundary, economy status and mode, harvester demand, active
home-owned remote operations, the latest bounded room-management failure, and
the room's inter-colony assistance state. Assistance output includes the
recipient deficit and recovery target, selected donor and current safe budget,
authorized/transferred energy, assigned creep, and the active denial,
suspension, completion, or recovery reason. See
INTER_COLONY_ENERGY_ASSISTANCE.md for policy and live checks.
The autonomy states are:
operational: essential local workforce and economy are healthy;recovering-workforce: essential workforce is incomplete but locally recoverable;recovering-economy: core workforce exists while the energy foundation rebuilds;blocked: the room cannot currently restore a harvester with its own spawn and available energy.
These commands format their output only when invoked. They do not create
cross-colony support requests, override autonomous assistance, or transfer
ownership. debug.creep(name) also includes the explicit assistance assignment
and phase when present.
debug.body(roomName?) reports current using the effective workforce used by
spawn planning: creeps already spawning count, while creeps inside their
replacement lead window do not. This can differ briefly from the number of live
creeps shown by other room and stats views during a handoff.
Colony Bootstrap
Colonization begins only with an explicit operator command:
debug.colonize("E48N14", "E48N13")
debug.colonization("E48N14")
debug.cancelColonization("E48N14")
debug.colonize() validates room names, visible bootstrapper ownership, target
non-ownership, duplicate/conflicting operations, and current GCL capacity. The
command itself is authorization and creates durable intent; it does not issue a
creep action or require debug.approve(). A capacity shortage is retained as an
auto-retried blocker.
debug.colonization() works without target visibility and reports operation
identity, authorization, phase and previous phase, blocker, remembered
controller ownership/reservation/RCL/downgrade/safe-mode state, acquisition and
pioneer assignments and demand, spawn plan and site progress, clearance,
milestones, terminal state, and the ten most recent bounded events. Clearance
may name either a selected-tile obstruction or a foreign spawn consuming the
claimed room's RCL spawn allowance.
debug.cancelColonization() is terminal and idempotent. It stops new bootstrap
demand, safely releases assignments, retains history, and never kills creeps or
destroys room state. Starting again requires a new command and operation ID.
There is intentionally no debug.resumeColonization(); temporary visibility,
energy, spawn, GCL, safe-mode, cooldown, replacement, and construction blockers
are reevaluated by the tick manager. Pioneer rows in debug.intents() use the
colonyBootstrap workflow and report current travel, withdraw, pickup, harvest,
dismantle, build, upgrade, or idle behavior. See
COLONY_BOOTSTRAP.md.
Screeps Lab Snapshot Export
The Screeps Lab exporter is operator-triggered and captures one visible owned
room as a versioned portable artifact. It uses RawMemory segments 70-79 and
stores only small status/control data in Memory.screepsLabSnapshot.
Use:
debug.beginLabSnapshot("E48N13")
debug.labSnapshotStatus()
debug.cancelLabSnapshot()
debug.clearLabSnapshot()
debug.beginLabSnapshot(roomName) validates the room, captures state during the
current tick, and starts publishing chunks over later ticks. It does not print
the full snapshot. debug.labSnapshotStatus() shows chunk progress, segment
IDs, byte count, checksum, and errors. Download completed artifacts locally with
npm run lab:snapshot:download -- --room E48N13.
See SCREEPS_LAB_SNAPSHOT.md for the schema,
segment reservation, lifecycle, downloader, limitations, and compatibility
policy.
Stats Export
Memory.stats is the repository-owned telemetry contract. The canonical metric
catalog, numeric mappings, counter/gauge guidance, and ownership boundary live
in STATS.md. Keep this section focused on debugging context and
console inspection.
collectStats() from src/stats/collect.ts runs once near the end of each tick
and writes the exported telemetry object to Memory.stats. The Screeps bot owns
only the projection from game state and internal Memory into Memory.stats; any
StatsD exporter, Graphite storage, or Grafana dashboard is downstream.
The stats collector receives the RoomContext snapshots produced by
RoomManager, so room telemetry reuses the same per-room scans where practical
instead of rediscovering common room state.
Memory.stats.cpu.profile records low-cardinality per-tick CPU measurements.
Memory.stats.cpu.governor exports bucket posture as a numeric enum:
0=open, 2=recovery, and 3=emergency; value 1 is retired.
Use cpu.profile.sections.* to compare main-loop phases and
cpu.profile.rooms[roomName].* to compare room-manager phases such as
remoteOperations, infrastructure, spawnPlanning, and creeps.
cpu.profile.sections.creepIndex isolates the single whole-creep indexing pass
that supplies all owned-room contexts and global role dispatchers.
The owned-room creep dispatch profile also emits stable role child series under
cpu.profile.rooms.<roomName>.creeps.<role> for supported creep roles. The
parent creeps value remains the full dispatch-section wall-clock cost, while
role children sum CPU by the creep's current room. This includes global scouts,
remote harvesters, remote reservers, remote construction builders, remote
haulers, colonizers, and colonization pioneers. Consequently, a bootstrap room
can have nonzero role children even when its ordinary RoomManager is deferred,
and role children are not expected to reconcile with the parent dispatcher
value. Global section totals and current-room role children are alternate views
of the same execution and must not be added together.
The loop deliberately reads Memory.creeps under
cpu.profile.sections.memoryParse before cleanup. This isolates Screeps memory
deserialization from cleanDeadCreepMemory, which now measures only the actual
stale-entry pass.
cpu.profile.sections.memoryCompaction measures the versioned persistent
Memory migration. It handles at most ten sorted room records per tick, records
its cursor under Memory.persistentMemoryCompaction, then runs bounded global
intelligence-retention maintenance every 500 ticks after migration. Live
observation refreshes continue enforcing per-dossier retention rules between
maintenance passes.
The CIA portion drops per-object hostile detail after 2,500 ticks, removes
unused names/owners/types from sighting summaries, and converts at most six
10,000-tick CIA events per room to compact tuples. debug.ciaEvents() hydrates
those tuples back into readable event objects. The staggered migration cost is
included in cpu.profile.sections.memoryCompaction.
The same bounded hook also hydrates the active logistics, planned-structure,
and infrastructure-position storage codecs early in the tick. Outside CPU
recovery it prepares newly replaced records again near the end of the loop;
recovery suppresses the managers that can replace those records, so the
redundant late pass is skipped. Debug helpers therefore continue seeing the
documented object and array contracts rather than storage tuples or packed
coordinate strings.
Across consecutive ticks in one global, a tick-local runtime cache reuses the
already hydrated network, planned-structure map, and route position arrays.
The compact serialized form remains the reset-safe source of truth; a global
reset simply pays one normal decode before repopulating the cache.
The fixed cpu.profile.rooms.<roomName>.hauler.* gauges split local hauler CPU
between task validation, urgent/link/route/fallback selection, and task
execution. Assigned remote haulers are covered by the fixed remoteExecution
gauge because they bypass the local role pipeline. These metrics intentionally
contain no creep names or target identifiers.
Maintenance pressure uses a bounded classification/count pass and reuses its
route-road value index for the full maintenance plan later in the same tick.
Empty haulers use room-context gauges to test for urgent delivery demand before
optional link gathering; they do not pathfind merely to answer that boolean
question.
updateColonyStats() from src/stats/colonyStats.ts runs once per tick for each
owned room managed by RoomManager and writes population planning telemetry to
Memory.rooms[roomName].stats.creeps. That room-memory cache is an input to the
stats collector and debug views, not itself the Grafana contract.
Scout execution is global because scouts can leave owned rooms. RoomManager
executes only room-bound economic roles: harvester, hauler, builder, upgrader,
and mineral miner. Scouts still appear in room snapshots, room debug output, population
stats, and spawn planning, but src/main.ts is the intended behavior dispatcher
for roleScout.run(). debug.intents() continues to report every living scout,
including scouts in adjacent unowned rooms.
The global loop disables Screeps' built-in attack notifications for scouts. Attack notifications for every other creep role retain the engine default.
Routine scout targets are the deduplicated union of rooms one exit from every
currently owned room; owned rooms are excluded. memory.homeRoom remains the
scout's lifecycle owner and does not limit this geographic frontier. Per-room
scouting demand and Launch Control leases remain independent, but the final
spawn admission gate permits at most one active or spawning scout colony-wide.
Memory.stats.rooms contains detailed operational telemetry only for visible
owned rooms. Observed-room telemetry is exported separately from persistent room
intelligence, so previously scouted rooms can remain visible in Memory.stats
after the scout leaves. Internal Memory may contain usernames, ids, room
positions, arrays, reasons, and manager state that are intentionally not
exported; inspect those through console helpers such as debug.room(),
debug.economy(), debug.terminal(), debug.maintenance(), and
debug.intents().
For quick debugging, remember these high-level encodings from STATS.md:
- booleans use
0=falseand1=true - economy status uses
0=starved,1=strained,2=stable,3=surplus - economy mode uses
0=normal,1=emergency - strategic surplus uses
0=inactive,1=building,2=sustained,3=overflow - link route mode uses
0=none,1=source-to-storage,2=storage-to-controller,3=source-to-controller - terminal energy state uses
0=none,1=empty,2=low,3=charging,4=balanced, and5=surplus - room intelligence energy state uses
0=normaland1=starvedin debug output
debug.economy(roomName?) prints the latest persisted economy assessment for
the requested owned room, or the first visible owned room when no room is
provided. It includes the current status, smoothed net energy trend, stored
energy and ratio, primary storage reserve thresholds, reserve deficits, spending
permissions, economy mode, emergency entry/recovery state, source/controller
container energy, strategic surplus state/reason/storage/source pressure,
construction remaining, important repair pressure, builder/hauler/upgrader
desired counts and reasons, last evaluation tick, and active population
cooldowns.
debug.body(roomName?) prints the body-scaling policy for the requested owned
room, or the first visible owned room when no room is provided. It uses the
latest persisted economy assessment and the same body policy as spawning. For
each role it shows current and desired population, selected option, selected
tech tier, cost, fallback status, highest technically eligible tier, highest
economically approved tier, selection reason, and every configured option with
its eligible/approved/affordable state and rejection reason.
debug.links(roomName?) prints current link classification and recent transfer
decisions for the requested owned room, or the first visible owned room when no
room is provided. It includes source/controller/storage/unclassified counts,
each link id, position, energy, free capacity, cooldown, classification reason,
whether controller feeding is currently allowed by economy policy, per-tick
transfer counts, sent/received/lost energy, usable and blocked route counts,
cooldown and receiver blockage counts, cumulative transferred energy, last
route, and recent sender to receiver decisions.
debug.logistics(roomName?) prints the hauler-facing logistics view for a
visible owned room. It includes completed source-to-storage route readiness,
available load-source-link and unload-receiver-link jobs with priorities
and reasons, per-tick hauler link-service counters, and haulers with link jobs
or persistent local route assignments. Local assignment lines include the
source node, destination node, stable route ID, and assignment tick. Use this
alongside debug.links() to distinguish structure transfer pressure from
creep service pressure.
debug.logisticsNetwork(roomName?) prints the shared room-owned logistics
network summary: known, operational, degraded, and blocked route counts,
aggregate route demand, source backlog, required carry capacity, assigned carry
capacity, capacity deficit, active route-backed jobs, last evaluation tick, and
bounded recent route events. It adds an infrastructure line with fully,
partially, degraded, and blocked support counts, completed/required components,
missing and damaged components, completed/required route roads, and aggregate
road completion ratio. Full logistics-network derivation is cadence-bounded;
the room summary also shows actual throughput, efficiency, average cycle/job
duration, source/destination/unavailable ticks, congestion, fatigue, fallback
and idle-capacity ratios, backlog delta, and healthy/watch/degraded/critical
counts. These values are read from persisted summaries and do not rerun route
policy.
route and infrastructure state shown here can lag live room state by up to the
configured logistics-network evaluation interval.
debug.logisticsNodes(roomName?) lists node identifiers referenced by the
persisted route summaries. Nodes are derived from adapter facts and are not
stored as large objects in Memory.
debug.logisticsRoutes(roomName?) prints deterministic route summaries sorted
by route id, including lifecycle state, preferred and active transport method,
source node, destination node, service class, source backlog, destination
demand, movable amount, estimated throughput, estimated cycle ticks, required
carry capacity, assigned carry capacity, capacity deficit, idle carry capacity,
plain-language capacity status, policy permission, current blockers, and
fallback route. Each line also shows infrastructure support state, worst
critical endpoint state, completed/required components, planned/approved and
site counts, missing and damaged components, road completion/site/missing/
damaged counts and ratio, infrastructure blockers, fallback usability, and the
last infrastructure validation tick.
Each route line additionally shows current service obligation, expected and actual throughput, EWMA efficiency,
estimated and actual cycle time, average job duration, observable collection-
and delivery-leg duration, blockage ratios, fallback rate, idle capacity/ratio,
backlog delta, congestion and fatigue
ratios, path recalculation count, retained traffic/chokepoint counts, health and
primary reason (including EWMA throughput only when service is obligated), window/confidence age, and path-cache status. Cache status
eligible-telemetry-only means evidence crossed the configured threshold;
custom serialized path following remains disabled. invalidated means the
stable route/endpoint, room, lifecycle, infrastructure-component, or road
coverage fingerprint changed.
debug.logisticsRoute(routeId, roomName?) prints one persisted route summary.
All logistics-network debug helpers are read-only; they inspect the
representation layer and do not assign haulers, transfer links, approve
infrastructure, change remote operations, or send terminal resources.
Route-backed jobs are stored under
Memory.rooms[roomName].logisticsNetwork.jobs; use debug.logisticsRoute() to
find the route id and inspect the bounded job record directly when needed.
Existing execution systems remain authoritative while the shared network
progressively becomes the route-demand and route-job source of truth.
Infrastructure support and logistics route lifecycle are separate. For example,
an operational link-assisted route can show infra=degraded and
fallbackUsable=true when its preferred link is missing but the conventional
creep route still works. critical=unverified means current visibility did not
prove the endpoint; it is not equivalent to completed. Planned, approved, and
construction-site component counts describe intent and are not runtime support.
debug.remote(homeRoomName?) is a shorthand for
debug.remoteOperations(homeRoomName?).
debug.externalOperations(homeRoomName?) lists the read-only normalized
external-operation view for one home room or all remembered home rooms. Each
line includes the stable external ID, kind, persistence, generalized lifecycle,
home and target rooms, resource, objective count, existing logistics route IDs,
and the current runtime authority.
debug.externalOperation(operationIdOrTargetRoomName, homeRoomName?) shows one
normalized operation in detail. It lists each selected source objective and its
stable identity, resource, and associated existing route IDs. Candidates with
no source selected by RemoteOperationsManager show zero objectives. Both
commands derive their output on demand and do not write remote-operation or
logistics Memory; debug.remoteOperations() remains the view of authoritative
pilot state.
debug.deposits(homeRoomName?) lists every currently known advisory highway
deposit opportunity, optionally filtered to one owned home. Each entry explains
deposit type, observation age and freshness, confidence, remaining lifetime,
cooldown, estimated yield, round-trip travel, spawn/workforce burden, required
carry capacity and current logistics deficit, protected-economy affordability,
route risk, Industry and inventory demand, cached executable Market value,
gross and burden-adjusted value, recommendation, and explicit blockers.
debug.deposits()
debug.deposits("E48N13")
debug.depositAuthority()
debug.depositAuthority("operator-approved")
debug.depositAuthority("autonomous")
debug.approveDepositOperation("highway-deposit:E48N13:E50N13:deposit-id")
debug.depositOperation()
debug.depositOperation("E48N13")
debug.externalOperation("E50N13", "E48N13")
debug.externalAllocation("E48N13")
debug.externalAuthority()
debug.externalAuthority("remote-energy", "operator-approved")
debug.externalControl("suspend", "E48N13-E48N14-source-id", "E48N13")
Unapproved deposit entries appear in the normalized external-operation helpers
as kind=highway-deposit, persistence=transient, and
authority=advisory-only. Deposit admission authority defaults to autonomous.
On a fresh 25-tick deposit assessment, autonomous policy may select the
highest-value eligible opportunity only while remoteDevelopment holds a
Launch Control lease. Lease expiry does not revoke committed work; the ordinary
operation lifecycle remains responsible for safety, economics, winding down,
and cargo evacuation. The most recently retired exact opportunity is excluded
from immediate readmission.
debug.depositAuthority(mode?) inspects or switches the persistent global mode
between autonomous and operator-approved. Manual
debug.approveDepositOperation(opportunityId) remains available in either mode.
Both paths accept only one exact currently eligible and allocated ID per home
room and create preparing
state. Neither bypasses live identity, CIA freshness, route, risk, economy,
essential-workforce, CPU, lifetime, cooldown, yield, actual-body repayment, or
Spawn Manager checks. The allocation layer rejects a second transient operation
for the same home room while allowing independent home rooms to proceed.
debug.externalAllocation(homeRoomName?) shows budget used versus available for
spawn energy, workforce, hauling capacity, CPU, infrastructure, and reservation
rooms. It lists deterministically ranked objectives, minimum requests, admitted
capacity, shared-overhead charges, incumbent state, and exact defer/preemption
reasons. This is the authoritative inspection surface for why an otherwise
eligible external objective did or did not receive optional investment.
Operation-owned hard admission blockers appear as deferred decisions with zero
allocated capacity; an incumbent marker never overrides those blockers.
debug.externalAuthority(kind?, mode?) inspects or changes the independent
policy for remote-energy and highway-deposit. Each kind supports manual,
recommendation-only, operator-approved, and autonomous. Manual and
operator-approved modes require exact approval for new work;
recommendation-only retains healthy incumbents but does not admit a new
objective; autonomous mode may admit only within the same allocator and native
safety gates. Existing deposit authority configured through
debug.depositAuthority() remains compatible with the deposit half of this
policy.
debug.externalControl(action, objectiveId, homeRoomName?) applies an exact
approve, suspend, resume, or retire override to a live remote-source or
deposit objective. Resume and approval return through full intelligence,
economy, risk, and allocation revalidation. Suspension preserves Memory and
causes assigned workers to evacuate carried resources. Retirement removes
future workforce and infrastructure priority; deposit retirement winds down
through cargo evacuation, while stale persistent records are pruned only after
the bounded retention interval and participant checks.
debug.externalPortfolio(homeRoomName?) explains the comparable ranking input
used by that allocator. For each candidate it shows the preserved native
score/value, final portfolio and effective allocator ranks, every signed
dimension, strategic-demand level, cached executable Market contribution,
performance adjustment and evidence, expected/measured return, exact minimum
capacity request, admission result, and compact evidence reasons. The view is
read-only and does not refresh discovery, Market orders, paths, or logistics.
debug.depositOperation(homeRoomName?) shows the runtime ID, lifecycle, age,
exact deposit/type, home/target, worker, selected body/cost/spawn ticks, route and
carry estimate, movement/delivery evidence, cooldown samples, lifetime, expected
versus harvested yield, in-transit and returned cargo, economics, suspension or
winding-down reason, and the eight bounded transitions. After retirement it
shows the one retained compact outcome. Runtime entries normalize with
authority=DepositOperationsManager and their existing resource-typed route ID.
No deposit command approves infrastructure or executes a Market action.
debug.remoteOperations(homeRoomName?) prints the remote-operation summary for
one owned home room, or all home rooms with remote-operation memory when no room
is provided. It includes candidate count, eligible/blocked/evaluating totals,
best score, compact room-pilot state, and one line per source objective with
state, marginal economics, exact workforce, production, throughput, carry,
backlog, infrastructure, and reason, followed by candidate-room summaries.
Owned rooms are excluded from adjacent remote candidates and removed from retained candidate records. Candidate reassessment is normally bounded by the configured 50-tick cadence. It can run earlier for adjacency changes or decision-relevant intelligence changes, but a dossier observation timestamp changing without a material ownership, source, infrastructure, threat, freshness, confidence, or score-input change does not trigger reassessment. Home operating-reserve fluctuations are sampled on the normal cadence. Active pilot operations retain an every-tick visible-risk gate; foreign ownership or creeps immediately force the full ownership and safety refresh instead of waiting for the normal five-tick lifecycle phase.
debug.remoteOperation(operationIdOrTargetRoomName, homeRoomName?) prints the
detailed read-only assessment for one target room or one source-objective id. A
target room prints every sibling source objective. Objective details include
home room, state, risk, selected source, assigned creep,
intelligence age/confidence, ownership/reservation, separate foreign-creep and
actionable-threat counts, tower readiness, tower energy/capacity, reason, last
safe/unsafe observations,
reactivation cooldown, remote source-container infrastructure state, build
order id, selected position, reason, hauling authorization, assigned hauler,
active/spawning hauler counts, hauling phase, exact container id and position,
visible container energy, carried remote energy, workload route/cycle/production
and required capacity, desired hauler count, assigned carry capacity, capacity
deficit, recommended remote logistics body cost/carry/move/capacity, throughput
capacity, replacement lead, route confidence, fallback reason, last workload
evaluation tick, hauling suspension reason, participant counts, container
visibility/fill, reservation remaining ticks, target ticks, renewal state,
active reserver count, harvested/hauled/delivered-home counters, gross delivery
ratio, in-transit energy, haul and reservation activity ticks, last
activity/delivery ticks, remote CPU, and telemetry. It does not approve
construction, attack, or mutate operator-controlled infrastructure.
debug.creep(name) and inspect(name) include remoteHauling memory for a
hauler assigned to the pilot. Use this to confirm the home room, target room,
operation id, source id, exact container id, phase, suspension reason, and trip
count.
A visible foreign creep with no active ATTACK, RANGED_ATTACK, HEAL,
WORK, or CLAIM parts should appear in the foreign-creep count while the
actionable-threat count remains zero. The operation may show conditional risk, but
it remains active and does not block the shared remote-road or construction
gate.
For live remote hauling verification:
- Run
debug.remoteOperation("<targetRoom>", "<homeRoom>")and confirm hauling is authorized, the operation is active, and there are no suspension blockers. - Run
debug.creep("<haulerName>")while the hauler moves throughtravel-to-remote,withdraw-remote,return-home, anddeliver-home. - Check
Memory.stats.rooms.<homeRoom>.remoteOperationsfor lifecycle, workforce, harvested/dropped/hauled/delivered-home energy, separated lifetime counters, container visibility, delivery ratio, in-transit energy, route distance, cycle ticks, required carry capacity, desired hauler count, assigned carry capacity, selected body metrics, route confidence/fallback flags, reservation state, haul and reservation activity, and CPU. - Suspend or invalidate the operation and confirm the hauler changes to
suspended-return-home, returns home with carried energy, delivers normally, and clears the assignment when empty.
Infrastructure planning helpers inspect and operate the configured authority boundary
for planned construction. debug.buildOrders() lists pending and recently
relevant build orders with copyable approval and rejection commands.
debug.buildOrder(id) returns complete JSON details for one order, including
plan revision and validation state. debug.pending() and debug.show(id)
provide the preferred cross-lifecycle ticket view. For a build-order ticket,
debug.approve(id) records manual approval only; policy authorization and
manual approval both place sites later during
normal room execution after revalidation. debug.reject(id) rejects the order
and starts a reconsideration cooldown. debug.cancel(id) is the generic
cancellation command, while debug.cancelBuildOrder(id) remains an alias; both
cancel without removing any site or structure.
debug.infrastructurePlan(roomName?) now inspects the retained generic
remote/defense lifecycle: cadence and dirty-domain state, remote/defense
plans and orders, remote-road state, link-network summaries, and recent events.
Use debug.infrastructurePlan(roomName, "detailed") for its persisted retained
state. Local authority is inspected through debug.colonyLayout(),
debug.localConstruction(), and debug.logisticsRoutes().
debug.colonyLayout(roomName?, visualize?) reports the persisted authoritative
fixed-local layout: planner version, stable anchor and transform, 50/30/20
weighted score, core/Lab/operational/pod/lane counts, terrain distortion, and
bounded exact blockers. Pass true as the second argument to draw the geometry
for the current tick. Gold letters are core structures, purple letters are
Labs, cyan letters are local operational endpoints, green circles are Extension
reservations, gray dots are shared lanes, the anchor is marked, and red circles
identify blockers. The command and visualization are read-only; the focused
local construction executor consumes these fixed slots and unified Road intent
without a retained local plan, order, or package copy. Visuals are
rendered directly from the persisted plan for one tick and do not create a
second visual cache or retain historical frames in Memory.
The report also shows complete blocker totals for retiringRoad,
migrationRequired, protectedConflict, and terrainImpossible. Each retained
blocker sample includes its classification; totals cover blockers omitted from
the bounded sample. The report also shows the fresh bounded Lab search range,
generated and zero-relocation candidate counts, relocation and authoritative
rejection totals, the dominant hard generation rejection, and an exact failure
reason when no candidate survives. Its bounded authoritative-layout-conflict
sample identifies Lab or service positions that overlap named non-Lab fixed
geometry; derived local lanes reroute around an accepted Lab footprint. Live
Extension obstructions remain no-relocation blockers. Persisted candidates and
legacy Lab plans belong to the separate operational Lab planner and do not
control authoritative colony-layout geometry.
debug.labCluster(roomName?) shows at most three
generated ten-Lab footprints, their stable reagent inputs, and exact Lab or
Extension relocation blockers. debug.reserveLabCluster() reserves one exact
candidate and pauses builder work on conflicting non-Lab sites without removing
anything. debug.releaseLabCluster() releases an unfulfilled operational reservation.
debug.reconsiderPlan(id, reason?) records a missing current coordinate
as operator-rejected and queues immediate reconsideration. The veto is shared
by sibling plans with the same room, target, structure type, purpose, and
anchor. Coordinated Labs use the dedicated footprint commands. The room must be
visible and the structure or site must already be absent. Ordinary destruction
recovery retains the old coordinate; this explicit command communicates that
the placement itself was unwanted.
Attempts and successful coordinate vetoes are retained in the bounded
debug.history() operator log.
debug.explainPlan(id) shows the selected candidate, alternatives, score
components, invalid-candidate summary, home room, target room, and remote
operation/source metadata when present. The room plan summary now includes every
authorized buildable structure type with completed,
site, active plan, pending, approved, blocked, allowed, remaining, and doctrine
state fields. Link-network details remain available where relevant.
debug.terminal(roomName?) prints current terminal readiness for the requested
owned room, or the first visible owned room when no room is provided. It
includes terminal energy state against the configured 250,000-energy standing
target, target delta, total store usage, send/receive readiness, visible
owned-room transaction-cost samples for a 1,000-resource transfer, the current
recommendation, and the largest stored resource stacks.
debug.maintenance(roomName?) prints the latest persisted maintenance summary
for the requested owned room, or the first visible owned room when no room is
provided. It includes budget mode, backlog counts by class, backlog score,
container and road health, known route-backed road count, current route-backed
maintenance candidates and repair backlog, maintenance energy spent this tick
and in total, and the current top-ranked job reason. It also derives the live
mixed-emergency recovery state: critical Container and catastrophic Road job
counts, outstanding Road sites, the deterministic Road-recovery builder, and
each local builder's current lane and task. Route-backed roads remain important
rather than critical and still obey the displayed budget mode.
When at least two unassigned local builders are available and critical Containers coexist with catastrophic Roads, one stable builder-name lane is reserved for Road recovery. The other builders distribute across unreserved emergency targets. The Road lane repairs catastrophic Roads first and then completes existing Road construction sites before ordinary construction. Remote-construction builders and active-defense Rampart emergencies are not reassigned into this lane.
Memory.stats.server.tick is derived from wall-clock time between consecutive
executions of the main loop, so it measures elapsed real time rather than
Game.time tick deltas. The first tick after deployment or a Memory reset
records only the current wall-clock timestamp and may not emit tick-speed gauges
yet.
hostileStructures[] stores strategically recent detailed hostile structure
intelligence from scout observations in internal room memory. Towers and spawns
are sorted first, structures with an energy store include current and maximum
energy, and per-object records expire after 2,500 ticks without confirmation.
debug.room(roomName) includes a compact hostileStructuresReport for quick
console review.
Screeps Profiler
The optional screeps-profiler integration is controlled by
enableScreepsProfiler in src/config/cpu.ts. When it is false, role
dispatchers retain their original function references. When it is true, the
following module-initialized function names are available for filtered profiler
runs:
RoomManager.assessEconomyRoomManager.runLinksRoomManager.runTerminalLogisticsRoomManager.createMaintenanceManagerMaintenanceManager.createRouteRoadValueIndexMaintenanceManager.buildRoadClassificationIndexMaintenanceManager.createPlanRoomManager.updateRoomIntelligenceRoomManager.runRemoteOperationsRoomManager.runRemoteReservationRoomManager.runInfrastructureRoomManager.runLogisticsNetworkRoomManager.updateColonyStatsRole.builder.runRole.harvester.runRole.hauler.runRole.hauler.taskRole.upgrader.runRole.scout.runRole.remoteHarvester.runRole.remoteReserver.runRole.remoteConstructionBuilder.runRole.remoteHaulingHauler.runRole.colonizer.runRole.colonizationPioneer.run
RoomManager.updateRoomIntelligence, RoomManager.runRemoteOperations, and
RoomManager.runInfrastructure now execute on separate stable five-tick room
phases. Their manual room CPU gauges are near zero on skipped ticks and show the
full pass on scheduled ticks; use a moving average of at least five ticks when
comparing their cost. Visible remote ownership or foreign creeps bypass the
remote-operation phase so safety refreshes run immediately. Colony bootstrap
remains inside the manual remoteOperations CPU section every tick but is no
longer inside the optional RoomManager.runRemoteOperations profiler wrapper.
For example:
Game.profiler.profile(50, "Role.hauler.run");
Game.profiler.profile(50, "Role.hauler.task");
Role.hauler.task is intentionally nested inside Role.hauler.run; the
existing manual hauler CPU telemetry remains active alongside these optional
profiler boundaries. Optional function wrappers register only when the Screeps
runtime is present, so importing an individual manager in an offline verifier
does not require Screeps runtime constructors or add a live per-call shim.
Maintenance route values and road classifications are cached for five ticks.
The cache is rebuilt when its bounded lifetime expires; road classification is
also rebuilt immediately when the owned-road or important-infrastructure
topology changes. Current structure health, repair-job eligibility, scoring,
and assignment selection are still evaluated every tick under
MaintenanceManager.createPlan.
Infrastructure diagnostic summaries are rebuilt whenever the staggered infrastructure manager runs, normally every five ticks with a stable per-room offset. Local-construction and logistics-road state use their owner-specific bounded summaries; the retained generic summary covers remote and defense work.
Routine economic and remote movement uses bounded built-in moveTo path reuse:
50 ticks for unchanged local logistics targets, room-transition approaches,
stable work targets, and remote legs. Movement is evaluated only
when an action is out of range; fatigue and in-range hauler calls return before
moveTo. Screeps replaces the cached path when the destination or requested
range changes, while the reuse timeout bounds stale geometry. Inspect the cost
under native Creep.moveTo, Room.findPath, and RoomPosition.findPathTo
profiler rows, plus the fixed per-room hauler movementExecution gauge.
When an unfatigued hauler repeatedly requests movement without leaving its tile,
the movement helper clears only its cached _move path and requests one fresh
path for that stationary episode. inspect(name) and debug.creep(name) expose
haulerMovementTarget and haulerMovementProgress; the latter shows
stationaryTicks, recoveryAttempted, and lastResult without emitting logs.
Inspecting Creeps
inspect(name)
Shortcut for inspectCreep(name) from src/debug/inspect.ts.
Parameters:
name: string- creep name fromGame.creeps.
Returns:
"Not found"ifGame.creeps[name]does not exist.- A formatted JSON string with:
role-creep.memory.rolestatus- status display object, for example{ "moving": "🏃" }pos- currentRoomPositionstore- carried resourcesfatigue- current fatiguettl-ticksToLivetask-creep.memory.tasklogisticsJob- assigned hauler link-service job, when presentsourceId- assigned harvester source id, when presentlocalHaulingRoute- persistent ordinary-hauler route assignment, including its source container or storage identity, when presenthaulerMovementTarget- current movement target and requested range keyhaulerMovementProgress- compact stationary-path recovery state, including the last observed tile and tick, stationary count, recovery latch, and latestmoveToresultintent- rawcreep.memory.intentintentDetails- formatted intent summary
task is a persistent per-creep assignment in the format action:id, such as
withdraw:abc123, build:abc123, repair:abc123, pickup:abc123, or
deliver:abc123. Builders, upgraders, and haulers prefer continuing these
assignments while the target remains valid, then clear them when the creep
changes work phase or the target is no longer usable.
New-colony recovery appears as workflow colonyRecovery. A withdraw action
means the builder is acquiring local energy for the stranded spawn; deliver
means it is filling an owned spawn or extension until the first local harvester
or hauler is alive.
Local haulers also keep compact haulerLoad provenance while carrying energy.
It identifies the acquisition source, acquisition kind, and tick; it is cleared
when the load is exhausted. Ordinary delivery selection rejects a destination
whose ID matches that source because returning the same load produces no useful
logistics state change. When this happens, the current intent reason names the
source/destination match, and
Memory.stats.rooms.<room>.haulers.coherence.preventedEnergyEchoes increments.
Controller containers remain valid withdrawal fallbacks and delivery buffers
when the source and destination differ and normal reserve policy permits them.
Hauler gathering may now show either withdraw:<id> or pickup:<id>. A
pickup task means the hauler is collecting dropped energy selected by the
logistics acquisition workflow. intentDetails shows the workflow/action,
dropped-energy target, selection reason such as nearby, significant, or
decay-urgent loose energy, and the latest pickup result. Haulers in delivery
mode keep deliver tasks and do not interrupt delivery to collect drops.
Hauler link-service assignments are stored separately in
creep.memory.logisticsJob. This keeps higher-level route/job identity visible
without replacing the existing action-level task assignment.
Ordinary local route assignments are stored in
creep.memory.localHaulingRoute. They preserve the route, source node, physical source (container or storage), destination node, and
assignment tick across repeated trips;
the action-level task still shows the current withdrawal or delivery endpoint.
Example:
inspect("Harvester123")
Sample output:
{
"role": "harvester",
"status": {
"moving": "🏃"
},
"pos": {
"x": 22,
"y": 18,
"roomName": "E48N13"
},
"store": {
"energy": 25
},
"fatigue": 0,
"ttl": 1372,
"sourceId": "5bbcabcd9099fc012e635123",
"intent": {
"role": "harvester",
"workflow": "harvester",
"action": "move",
"targetId": "5bbcabcd9099fc012e635456",
"targetType": "container",
"reason": "moving onto assigned source container",
"tick": 12345678
},
"intentDetails": "workflow: harvester\naction: move\ntarget: container 5bbcabcd9099fc012e635456\nreason: moving onto assigned source container\nresult: unknown\ntick: 12345678"
}
debug.creep(name)
Detailed creep inspection from src/debug/debug.ts.
Parameters:
name: string- creep name fromGame.creeps.
Returns:
"Creep not found"ifGame.creeps[name]does not exist.- A formatted JSON string with everything from
inspect(name), plus: namehitsas"current/max"
Example:
debug.creep("Builder123")
Use this when health matters or when comparing several named creeps.
Inspecting Room State
debug.room(roomName?)
Returns a RoomManager-style room summary for a visible owned room. Pass a room name to inspect a specific visible room.
Parameters:
roomName?: string- optional visible room name. Defaults to the first owned visible room.
Current limitation:
- Requires the selected room to be visible in
Game.rooms.
Returns a formatted JSON string with:
nameenergyAvailableenergyCapacitycontroller- level, progress, progress total, and ticks to downgradesourcesspawnscreeps- counts grouped by roleconstructionSitesstructurescontainerssourceContainerscontrollerContainersprimaryEnergyReserve- storage id, energy, free capacity, and total capacity when completed owned storage existsroadshostilestowersintelligence- the persistent room intelligence stored inMemory.rooms[roomName].intelligence
CIA Debugging
The CIA never makes decisions. It provides intelligence.
Use debug.cia() for a compact index of known room dossiers:
debug.cia()
Use either form for a room-specific dossier readout:
debug.cia("E48N14")
debug.dossier("E48N14")
debug.assessment("E48N14")
debug.ciaEvents()
debug.ciaEvents("E48N14")
The room readout includes last observation ticks, confidence, ownership,
ownership transitions, resource survey data, current or stale hostile
observations, advisory strategic assessment, and the latest status summary.
debug.assessment(roomName) refreshes and returns that room's advisory
assessment, including category scores, category confidence, overall score,
recommendation, recommendation reason, freshness, and recent CIA events.
Example:
debug.room()
debug.room("E48N13")
Sample output:
{
"name": "E48N13",
"energyAvailable": 300,
"energyCapacity": 550,
"controller": {
"level": 3,
"progress": 4520,
"progressTotal": 135000,
"ticksToDowngrade": 19876
},
"sources": 2,
"spawns": ["Spawn1"],
"creeps": {
"builder": 1,
"harvester": 2,
"hauler": 1,
"upgrader": 1
},
"constructionSites": 2,
"structures": 18,
"containers": 3,
"sourceContainers": 2,
"controllerContainers": 1,
"towers": 1
}
debug.spawn(roomName?)
Returns a spawn summary for the first spawn in deterministic name order.
Parameters:
roomName?: string- optional owned room filter. When omitted, the helper uses the first owned spawn in deterministic name order.
If no matching spawn exists, the helper returns a descriptive message.
Returns a formatted JSON string with:
spawning- name of the creep currently being spawned, ornullenergy- energy currently stored in the spawn
Example:
debug.spawn()
Sample output:
{
"spawning": "Hauler12345678",
"energy": 300
}
debug.body(roomName?)
Returns the current body-tier policy decision for a visible owned room.
Parameters:
roomName?: string- visible owned room name. When omitted, the helper uses the first visible owned room.
Returns a newline-delimited summary with:
- CPU population mode, bucket, prior-completed-tick utilization EWMA, and last mode transition
- economy status, mode, smoothed trend, spawn energy, reserve thresholds, and reserve spending permissions
- local harvester full-tier lock state, primary Storage reserve ratio and threshold, approved-body wait state, and recovery fallback state
- for each role, uncapped, RCL-capped, CPU-capped, safety-floor-adjusted, operator-override, and final effective desired count; selected option, tier, cost, size, WORK/CARRY capacity, fallback state, highest technically eligible tier, and highest economically approved tier
- per-option eligibility, economic approval, current affordability, and reason
Example:
debug.body()
debug.body("E48N13")
Use this when a room reaches RCL 5 but does not choose a larger body, or when a
selected body falls back because current spawn/extension energy is temporarily
low. It is also the primary explanation for population throttling: inspect the
uncapped/RCL/CPU/safety-floor/operator/effective sequence and the appended cap,
safety-floor, or operator-authority reason.
The current bucket bands and observed-utilization thresholds are reported by
debug.gov() and Memory.stats.cpu.bucketTargets / .targets. For healthy mature rooms,
confirm lines such as
harvester full-tier lock: yes and
storage reserve: 61234/1000000 (6.1%, threshold 5.0%) before diagnosing a
local harvester down-tier.
debug.economy(roomName?)
Returns the latest persisted economy assessment for an owned room.
Parameters:
roomName?: string- visible owned room name. When omitted, the helper uses the first visible owned room.
Returns a newline-delimited summary with:
- economy status and operating mode
- smoothed net energy trend
- stored energy, primary storage energy, reserve thresholds, deficits, and spending permissions
- emergency state and recovery threshold
- strategic surplus state, reason, storage pressure, source overflow pressure, and spending level
- source and controller container energy totals
- construction remaining and important repair count
- CPU population mode, bucket, utilization EWMA, and last transition
- effective builder, upgrader, and hauler counts with economy baselines and population-plan reasons
- last full evaluation tick and active population cooldowns
The assessment is refreshed on the economy manager cadence. Fast-changing fields such as stored energy and construction remaining may update between full role-count evaluations, while desired-count reasons can reflect the last full evaluation tick.
Example:
debug.economy()
debug.economy("E48N13")
Sample output:
E48N13 economy: strained (normal)
trend: -0.2 energy/tick
stored: 95727/1010000 (9%)
primary storage: 89479/1000000
reserve: 89479/10000 target, min 3000, surplus at 15000
reserve deficits: operating=0, target=0, surplus=74479
reserve permissions: withdraw=yes, optional=yes, surplus=yes, store=no, controller=yes
emergency: no, entered=n/a, exit reserve=5000, reason=storage reserve is above emergency exit threshold
strategic surplus: inactive, active=no, entered=n/a, duration=0, spending=0
strategic reason: surplus signals are below entry thresholds
storage pressure: fill=9%, free=910521
source overflow: backlog=26%, count=0, risk=no
source containers: 648
controller containers: 5600
construction remaining: 1258
important repairs: 39
builder desired=2: 1258 construction remaining, 39 important repairs
upgrader desired=1: strained economy protects reserve and keeps controller work at baseline
last evaluation: 81458087
cooldowns: {"builder":{"increaseUntil":81453069,"decreaseUntil":81452434},"upgrader":{"increaseUntil":81458087,"decreaseUntil":81458062}}
Infrastructure and local construction
Colony layout is the authoritative fixed-local geometry source. It is generated inside the bounded infrastructure cadence and revalidated at most once per 100 ticks unless its version, anchor, or Lab recommendation changes. Local Roads come from the derived merge of layout lanes and logistics-owned arterial intent.
debug.localConstruction(roomName, itemId?, action?) is the local execution and
manual-approval surface. Autonomous authority admits exact desired items
directly. Manual authority requires an exact current fingerprint under
localConstructionApprovals; old build orders and packages are never consulted.
The list includes the last execution item/outcome and reports current-RCL or
structure-capacity deferrals per item. Authoritative Lab items use their colony
layout coordinates directly, have unique coordinate IDs, and do not require a
separate operational Lab-cluster reservation.
The generic plan/order commands remain for remote source Containers and defensive Walls/Ramparts:
debug.buildOrders()
debug.buildOrder("E48N13-buildOrder0001")
debug.pending("E48N13")
debug.show(17)
debug.approve(17)
debug.reject(17, "unsafe remote placement")
debug.cancel(17, "no longer needed")
debug.infrastructurePlan("E48N13")
debug.infrastructurePlan("E48N13", "detailed")
debug.remoteRoads("E48N13", "E48N14")
debug.remoteConstruction("E48N13")
Remote Roads retain their dedicated exact approval lifecycle. Package commands, colony-layout rollout controls, and assisted Extension migration commands were removed by the completed infrastructure cutover.
infrastructureCutoverCleanupVersion = 1 filters existing mixed Memory once.
It preserves colony layout, focused local-construction state, logistics road
planning, remote source Containers/Roads, and defense records. It removes
fixed-local plan/order mirrors, all work packages, and transitional rollout and
migration state. The cleanup is bounded and idempotent.
debug.towers()
Returns a concise summary for every visible owned tower.
Parameters:
- None.
Each tower includes:
- energy as
"current/max" - mode:
Attack,Heal,Repair, orIdle - target when the tower would act this tick
Tower repair summaries follow src/config/towers.ts and only show roads or
containers when the tower has enough energy to repair.
Example:
debug.towers()
Sample output:
E48N13 Tower1
Energy: 940/1000
Mode: Repair
Target: road (52%)
Inspecting Intent
Each action or workflow can write creep.memory.intent. Intent explains what
the creep most recently tried to do and why.
Intent fields:
role- creep role at the time the intent was written.workflow- workflow or role path, such asbuilder,builderWork,haulerDelivery, oracquireEnergy.action- intended action, such asharvest,withdraw,pickup,deliver,build,repair,upgrade,move,work, oridle.targetId- target object id when the target has one.targetType- readable target type, such assource,container,spawn,extension,controller,road,energy drop,<structure> site, or<role> creep.reason- human-readable selection reason.result- Screeps result name, such asOK,ERR_NOT_FOUND, orERR_NOT_IN_RANGE.tick-Game.timewhen the intent was written.
Scout cross-room movement uses a fixed-size creep.memory.roomTransition
record with approaching, crossing, and settling phases. A successful
movement return code records only an accepted intent; the following tick's
observed room and position establish actual progress. After entry, the scout
records arrival and clears its mission target before settlement takes movement
ownership. Settlement prefers the straight inward tile, then either inward
diagonal, and uses bounded retries and a limited in-room fallback. The temporary
transition record is deleted after settlement, target replacement, hostile
retreat, or exhausted recovery.
When the requested destination is observed on an edge, its live room and position replace stale source-side crossing and recovery state before movement. Settlement remains the only movement owner for that tick; repeated transition calls in the same tick reuse the first result without issuing another move. Skipped CPU ticks do not alter the record, and normal role behavior resumes only after the creep is observed on a destination interior tile.
Scouts also prefer exits whose destination side has at least one
terrain-walkable inward candidate before committing to a target room. Blocked
routes and hostile scout rooms are tracked temporarily in
Memory.scoutAvoidRooms with a reason and expiration tick.
Approach paths to a selected exit are restricted to the current room with a
bounded operation budget. This prevents Screeps pathfinding from treating an
adjacent room as a shortcut to another edge of the same room; genuinely
unreachable exits continue through the bounded alternate-exit recovery.
Expired entries are pruned automatically so avoided rooms are retried later
instead of being banned permanently. Hostile rooms clear the scout's active
target when retreat begins, so they are revisited by the normal room
intelligence refresh cycle rather than by an immediate enter/exit loop. Scout
retreat intents include the hostile room, the current room, and the room or exit
the scout is retreating toward; the movement return code remains in result.
Hostile avoid entries expire after 100 ticks; blocked exits are retried after
500 ticks. When every current scout target is temporarily avoided, the scout
caches the earliest avoidance expiration (or earlier intelligence-refresh
deadline) and remains cheaply idle until that tick. The backoff is discarded if
its room or home-room context changes, its avoidance snapshot changes, the scout
is moved onto a room edge, or the cached tick is invalid or expires.
debug.intents()
Returns one line per creep, sorted by creep name.
Parameters:
- None.
Each line contains:
- creep name padded to 16 characters
- action padded to 10 characters
- target type padded to 12 characters
- reason
Creeps that are still being spawned report spawning as their action until
their role code runs and records an intent.
Example:
debug.intents()
Sample output:
Builder12345678 build container site selected construction site: container
Harvester123456 harvest source continuously harvesting from assigned source container
Hauler12345678 deliver spawn spawn needs energy
Upgrader123456 withdraw container selected preferred container: 742 energy, 74% full
Use debug.intents() for a quick room-wide scan, then use debug.creep(name)
or inspect(name) for the exact target id, result, store, TTL, and status.
Console Logs And Visuals
The main loop keeps routine console output intentionally low-noise. Most
colony state is summarized by the room dashboard in src/debug/dashboard.ts
instead of printing raw creep counts every tick.
Debug logging mode
Debug logging mode is stored in Memory.debug.enabled.
debug.enable()turns on per-tick dashboard logging.debug.disable()returns dashboard logging to the normal interval.debug.status()shows whether debug logging is enabled.
Example:
debug.enable()
debug.status()
debug.disable()
Room dashboard
logRoomDashboard(context, economyAssessment, maintenanceSummary) emits one
multi-line room summary every 25 ticks by default. When Memory.debug.enabled
is true, it emits every tick. The first line is Dashboard <roomName> so
concurrent multi-room blocks remain attributable to their room.
Dashboard fields:
Dashboard <roomName>- owned room represented by this block.[T<tick>]- currentGame.time.RCL<n>- room controller level, orRCL0if no controller is visible.⚡<available>/<capacity>- room energy available and capacity.👥 H<n> Ha<n> U<n> B<n>- creep counts by role:H- harvestersHa- haulersU- upgradersB- builders🏗<n>- construction site count.src:<energy/capacity,...>- containers within range1of room sources, orsrc:none.ctrl:<energy/capacity,...>- controller-area containers, orctrl:none.🛡 tower:<energy/capacity,...>- owned tower energy, shown only when towers exist.spawn:<status>-idle, current spawning creep role, orunknown.
Example:
Dashboard E48N14
[T123456] | RCL3 | ⚡450/800 | 👥 H2 Ha1 U2 B2 | 🏗6 | src:1850/2000,1980/2000 | ctrl:340/2000 | 🛡 tower:720/1000 | spawn:hauler
Event logs
The main loop also emits these event-oriented console messages:
Clearing non-existing creep memory: <name>when stale creep memory is deleted.Started spawning <name>whenspawn.spawnCreepreturnsOK.Failed to spawn <name>: <result>when spawning fails for a non-busy result.- New creeps use
preferredSpawnDirectionsfromsrc/config/spawning.tsso the spawn can bias creeps away from local pinch points.
Spawn failure result names currently include:
OKERR_BUSYERR_NOT_ENOUGH_ENERGYERR_INVALID_ARGSERR_RCL_NOT_ENOUGH- Unknown codes are printed as
UNKNOWN_RESULT_<code>.
Room visuals:
- While
Spawn1is spawning, the room displays🛠️<role>next to the spawn. - Spawn activity visuals are refreshed every tick while spawning, but no per-tick spawn status console message is emitted.
- Movement calls use path visuals:
- harvest and withdraw paths:
#ffaa00 - deliver, build, repair, and upgrade paths:
#ffffff
Status Meanings
Statuses are stored in creep.memory.status and shown with creep.say().
inspect(name) and debug.creep(name) display them as an object like
{ "harvesting": "⛏️" }.
| Status | Emoji | Meaning |
|---|---|---|
building |
🚧 |
The creep successfully built a construction site. |
dropping |
💧 |
The creep is dropping energy. |
harvesting |
⛏️ |
The creep successfully harvested a source. |
hauling |
🚚 |
The creep successfully transferred energy. |
idle |
😴 |
The creep had no usable target or no work to do. |
moving |
🏃 |
The creep is moving toward its current target. |
pickingUp |
💰 |
Reserved status for pickup behavior. No current action sets it. |
repairing |
🔧 |
The creep successfully repaired a structure. |
scouting |
👁️ |
The scout is observing or moving through scout workflow. |
upgrading |
⚡ |
The creep successfully upgraded the controller. |
withdrawing |
📦 |
The creep successfully withdrew energy. |
Movement may append a second intent emoji to 🏃:
| Movement Say | Meaning |
|---|---|
🏃⛏️ |
Moving to harvest or to an assigned source container. |
🏃📦 |
Moving to withdraw from a container or storage. |
🏃🔋 |
Moving to an extension. |
🏃🏠 |
Moving to a spawn. |
🏃⚡ |
Moving to a controller. |
🏃🚧 |
Moving to build a construction site. |
🏃🔧 |
Moving to repair. |
🏃🚚 |
Moving to deliver. |
Common Debugging Workflow
Start with the room-wide view:
debug.intents()
debug.room()
debug.spawn()
debug.economy()
Then inspect the specific creep:
debug.creep("CreepName")
Check these fields first:
rolestatusstore.energytasksourceIdintentDetailsfatiguettl
Why is this creep idle?
- Run
debug.creep("<name>"). - Read
intentDetails.reasonandintentDetails.result. - If
resultisERR_NOT_FOUND, the role could not find a valid target. - Use role-specific checks:
- Harvesters: verify a source exists and
sourceIdresolves. - Builders: verify energy, construction sites, damaged roads or containers, or controller access.
- Haulers: verify containers have energy or delivery targets need energy.
- Upgraders: verify energy and controller access.
Common idle reasons:
no active source foundno energy target foundno delivery target foundno usable container targetfull hauler has no delivery targetno spawn, extension, builder, or upgrader needs energyno construction site foundno repair target foundno controller found
Why isn't a hauler delivering?
Use:
debug.creep("Hauler123")
debug.economy()
debug.spawn()
debug.intents()
Check:
store.energy: if0, the hauler should be withdrawing, not delivering.memory.hauling: haulers switch to delivery after carrying any energy and switch back when empty.task: active delivery tasks are stored asdeliver:<targetId>.intentDetails.reason:gathering energy before deliverymeans the hauler is still filling.selected preferred container: ...means it found a strong withdrawal target.selected fallback container: ...means it found a weaker container.no usable container targetmeans no container has energy.spawn needs energyorextension needs energymeans spawn logistics are active.<role> needs <n> energymeans the hauler is refilling a working builder or upgrader.no spawn, extension, builder, or upgrader needs energymeans there is no eligible delivery target.
Haulers deliver in this priority order:
- Spawn or extension with free capacity.
- Working builders with at least
50free energy capacity. - Working upgraders with at least
50free energy capacity.
Builders and upgraders are not delivery targets while their status is
harvesting, pickingUp, or withdrawing.
Why won't a builder build?
Use:
debug.creep("Builder123")
debug.room()
debug.economy()
debug.remoteConstruction()
Check:
store.energy: empty builders runacquireEnergy.memory.building: builders enter work mode when full and leave it when empty.constructionSitesindebug.room()output:0means there is nothing to build.intentDetails.reason:builder needs energymeans the builder is refilling.builder has energymeans it is in the work workflow.assigned approved remote construction <id>means the home room gave one ordinary builder an approved remote source-container or remote-road job.building approved remote source containermeans the builder is executing that exact remote assignment.building approved remote roadmeans the builder is executing an approved remote-road construction site in the active remote target room.withdrawing from approved remote source containermeans the empty assigned builder is refilling from the exact approved completed remote source container. For road jobs this is the route's remote endpoint container, not the current road-site tile.picking up energy dropped at approved remote construction sourcemeans the empty assigned builder is using dropped energy adjacent to the authorized remote source, usually remote harvester overflow.harvesting from approved remote construction sourcemeans the empty assigned builder is harvesting only the source tied to that approved remote container job.returning to <room> to refill for remote constructionmeans the builder is out of local usable energy or its remote assignment is suspended and it is walking home.continuing from completed remote container repair to approved remote roadmeans container repair reached the configured continuation target and the same builder was reassigned to a valid same-operation road site in the remote room.repairing approved remote source containermeans the assigned builder is repairing only the exact completed container tied to its approved remote construction assignment after construction work is gone. Completed remote source-container repair starts below60%hits and continues until80%for the already-assigned builder.selected construction site: <type>means it found build work.<structure> hits <n>%, below <threshold>%means repair was chosen.no build or repair targetsmeans it is falling back to controller upgrading.
Builder work order:
- Continue a valid approved remote source-container assignment, if present. This includes exact-container repair only for that assignment when no remote construction site remains.
- Build construction sites, preferring container sites.
- Repair containers below
50%. - Repair roads below
70%. - Upgrade the controller.
If approved remote road sites are not being built, use:
debug.intents()
debug.remoteConstruction("E48N13")
debug.remoteRoads("E48N13", "E48N14")
debug.creep("Builder123")
debug.buildOrders()
debug.infrastructurePlan("E48N13")
debug.infrastructurePlan("E48N13", "detailed")
Check:
debug.intents()shows whether any builder is already assigned to remote construction. A line likerepair container repairing approved remote source containermeans the current remote builder slot is occupied by the approved remote source-container assignment.debug.remoteConstruction(homeRoomName)identifies the dispatch state: assigned builder, waiting for approval, waiting for site placement, waiting for an eligible builder, or the first validation/policy blocker.debug.remoteRoads(homeRoomName, targetRoomName)should show the route as approved while active road construction sites remain. A route is complete only whencompletedequalsrequired,sites=0, andmissing=0; active sites are work for the remote builder, not completed road tiles.debug.creep(builderName)showsmemory.remoteConstruction.constructionKind,plannedStructureId,structureType, andintentDetails.reasonfor the assigned builder.debug.buildOrders()anddebug.infrastructurePlan(homeRoomName)are useful when dispatch says the route is waiting for approval, waiting for site placement, or invalid.
Remote construction dispatch currently assigns at most one ordinary builder per home room. If that builder is repairing the approved remote source container, approved remote-road work waits until the container repair assignment clears. Healthy remote containers should not hold that slot; check the container hits if the assigned builder keeps repairing one.
Why isn't a creep harvesting?
Use:
debug.creep("CreepName")
debug.intents()
Check:
role: only harvesters always prefer harvesting. Builders and upgraders first try stored energy before falling back to harvesting.sourceId: harvesters should have an assigned source id.intentDetails.result:ERR_NOT_IN_RANGEmeans it is moving to the source.OKwith statusharvestingmeans harvesting is working.ERR_NOT_FOUNDwithno manager-assigned sourcemeansSpawnManagerhas not assigned the harvester yet.ERR_NOT_FOUNDwithmanager-assigned source is not visible or no longer existsmeans the savedsourceIddid not resolve this tick.ERR_NOT_FOUNDwithno active source foundmeans no active source was reachable.fatigue: high fatigue can make movement look stalled.
Harvester behavior:
SpawnManagerowns harvester source allocation and rebalancing. Harvesters execute their assignedsourceIdand idle for the current tick if that assignment is missing or unresolved.- If a container exists within range
1of the assigned source, the harvester moves onto the container and continuously attempts to harvest there. It does not spend normal source-container ticks transferring carried energy; harvested overflow is allowed to fall onto the tile and enter the container underneath. - If no source container exists and the harvester has free capacity, it harvests the assigned source.
- If no source container exists and the harvester is full, it delivers to spawn or extensions as a bootstrap fallback.
Harvest telemetry:
Memory.stats.rooms[roomName].energy.harvest.capacity.tickshows theoretical assigned harvester capacity from activeWORKparts, capped at10energy/tick per source.Memory.stats.rooms[roomName].harvesters.activecounts living, non-spawning harvesters;successfulHarvestscounts successful harvest actions;harvestAttemptscounts all harvester harvest calls; andfailedHarvestscounts harvest calls returning anything other thanOK.Memory.stats.rooms[roomName].harvesters.movingcounts harvesters moving to their assigned source or source container.idlemeans the harvester did not move, harvest, repair, or transfer this tick.repairingis currently expected to stay0because harvesters do not perform repair work.Memory.stats.rooms[roomName].harvesters.resultsbreaks harvest attempts into numeric result buckets such asok,notInRange, andnotEnoughResources.Memory.stats.rooms[roomName].harvesters.expectedHarvestEnergy,energyHarvested, andmissedHarvestEnergydistinguish expected harvest from actual successful harvest.Memory.stats.rooms[roomName].harvesters.unresolvedSource,notOnContainer,noSourceContainer,sourceEmpty, andcapacityBlockedseparate missing assignments, walking to the source container, bootstrap mining without a completed source container, empty sources, and full creep/container capacity.Memory.stats.rooms[roomName].sources[sourceId].harvest.tickshows estimated actual harvested energy per visible source for the current tick. Neighboring fields under the same path expose attempts, failures, expected/missed energy, assigned harvester count, activeWORKparts, source energy, regeneration, and source-container capacity.Memory.rooms[roomName].harvest.byName[creepName]contains transient, current-tick diagnostics for each living harvester.stateuses0=idle,1=harvest,2=move,3=repair,4=transfer, and5=unresolved source. This operational map is deliberately excluded fromMemory.statsto avoid creating permanent telemetry series for unique replacement-creep names.
For a 16-to-8 drop, check these first:
capacity=16,energyHarvested=8,harvestAttempts=1,moving=1: one harvester moved instead of attempting to harvest.harvestAttempts=2,successfulHarvests=1,failedHarvests=1: one harvester attempted and failed; inspectresults.*and per-sourcefailed.results.full>0pluscapacityBlocked>0: the creep and container under it had no harvest capacity.sourceEmpty>0or per-sourceenergy=0: the source was empty or regenerating.capacityBlocked>0with expected energy0: the creep and container under it had no free energy capacity.- In transient room harvest memory, normal source-container harvesters should
remain in
state=1with repeated harvest attempts rather than alternating withstate=4;state=4should only appear for the no-container bootstrap delivery fallback.
Why isn't the spawn producing creeps?
Use:
debug.spawn()
debug.spawnPlan()
debug.spawnOverrides()
debug.body()
debug.room()
debug.intents()
Check:
spawningindebug.spawn()output: if set, the spawn is already busy.energyindebug.spawn()output andenergyAvailableindebug.room()output: current available energy must afford the selected body.debug.body()output: confirm the role has an economically approved body, whether current energy can afford it, and whether the policy selected a recovery fallback or is briefly waiting for an optional larger body.debug.spawnPlan()output: compare automatic, RCL-capped, CPU-capped, safety-floor-adjusted, operator-override, and final effective counts.debug.spawnOverrides()output: confirm that a deliberate final count has not temporarily or indefinitely replaced automated demand.- Console logs:
Started spawning <name>means spawning succeeded. The log includes role, selected body option, tier, and whether a fallback body was used.Failed to spawn <name>: ERR_NOT_ENOUGH_ENERGYmeans the selected body was not affordable at the time of spawning.- No failure log can mean no role is below its desired count, or no body could be chosen.
Spawn priority and desired counts:
harvester: exactly one per source, recovery-safe bodies first; the five-WORKsource-saturatingbody is the high tier and remains gated by source throughput and economy health.defender: zero normally; one survival-priority melee defender when the room defense assessment says towers and active defenders are inadequate.mineralMiner: one mature miner when the RCL 6 mineral operation is eligible; this optional role is considered before logistics and remote-operation demand.hauler: zero until source containers exist, then one baseline hauler; two or three only when source containers or sustained destination demand show clear logistics pressure.upgrader: one baseline upgrader; two or three only when stored energy is healthy and active builder demand is not competing for energy.builder: zero when there are no construction sites or builder-managed repairs; one to three as construction and road/container repair backlog grows.scout: no scout before RCL 3; from RCL 3 onward, one low-priority[MOVE]scout when adjacent room intelligence is missing, stale, or needs a metadata refresh. Per-room planning still counts living scouts assigned bymemory.homeRoom, even when those scouts are outside the owned room; final spawn admission prevents those plans from producing more than one active or spawning scout across the colony.
debug.defense(roomName?)
Returns the latest persisted owned-room defensive assessment without recalculating combat policy.
debug.defense()
debug.defense("W1N1")
The output includes posture and reason, actionable hostile pressure, shared tower target and adequacy, desired/active defenders, critical-rampart minimum hits and exposure, and safe-mode policy, recommendation, eligibility, and retry state. Hostile usernames and free-form reasons are intentionally kept out of numeric stats.
Planned Debug Helpers
Potential future helpers include deeper stale-memory/task/source inspection,
room visual toggles, and more granular workflow profiling. Do not document a
planned helper as an available command until it is implemented and registered
in debug.help().
Adding New Debug Commands
Debug documentation is part of the codebase, not optional notes.
Whenever a new console helper, global command, inspect function, profiler, visualization helper, or debugging utility is added, both of the following must happen in the same pull request:
- Implement the feature.
- Update
docs/DEBUG.md.
The documentation update should include:
- command name
- parameters
- return value
- example usage
- sample output where useful
- any status, emoji, memory, visual, or console-log behavior it introduces
If a helper is removed or renamed, update this file in the same pull request.