Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chunk storage

Verified against Minecraft 26.3 · Part IV · A chunk nobody needs any more is dropped from the world and written to disk, and the server thread never waits for it.

You walk away from your base. Soon after, the chunk you were standing in is no longer reachable from any ticket, its loading level climbs past ChunkLevel.MAX_LEVEL, and a queued task takes a snapshot of it, hands that to a worker to turn into NBT, and hands that to a lane that compresses it and finds it somewhere to live in r.X.Z.mca. Nothing about that is surprising. What is surprising is that the chunk was almost certainly written several times before you left, and that none of those writes was anybody’s idea. A chunk you keep changing is written by a background sweep roughly every ten seconds — ChunkMap.saveChunksEagerly, at most ChunkMap.CHUNK_SAVED_EAGERLY_PER_TICK (20) chunks a tick, only while fewer than ChunkMap.MAX_ACTIVE_CHUNK_WRITES (128) writes are in flight, each chunk no sooner than ChunkMap.EAGER_CHUNK_SAVE_COOLDOWN_IN_MILLIS (10 000 ms) after its last — and the autosave everyone thinks of as the save comes every five minutes of the server’s scheduled time, at any steady /tick rate. Almost every write of your world is one nobody asked for.

The cast

classwhat it decidesthread
ChunkMapwhich chunks are dirty, at which of four moments each is written, and whether a half-generated chunk may overwrite a finished one — it is the region/ store, because it extends SimpleRegionStorageServer
SerializableChunkDatathe chunk file as a record: what gets copied while the world is frozen and what gets encoded afterServer copies, a Worker-Main-n encodes
IOWorkerone store’s single lane, and the write-behind map that lets a read answer from a write that has not landedany IO-Worker-n, one task at a time
RegionFileStoragewhich r.X.Z.mca files are open — an LRU of RegionFileStorage.MAX_CACHE_SIZE (256)the IO lane
RegionFilethe sector allocator and the two header tables of one 32×32-chunk file, and the order the bytes land inthe IO lane
EntityStoragethe entities/ store: what a chunk’s mobs cost to write, and that they are rebuilt on the Server threadServer builds and parses, IO lane writes
SectionStoragethe poi/ store under PoiManager: which sections are dirty, and the one load that blocksServer
MinecraftServerwhen the next autosave falls, and whether every region file is opened with DSYNCServer

Copy on the server, encode on a worker, write on the IO lane

flowchart LR
    S["the Server thread:<br/>SerializableChunkData.copyOf"]:::server
    W["a worker:<br/>SerializableChunkData.write"]:::worker
    F["the IO lane, foreground:<br/>IOWorker.store"]:::disk
    B["the IO lane, background:<br/>IOWorker.storePendingChunk"]:::disk
    R["RegionFileStorage.write"]:::disk
    D[("r.X.Z.mca")]:::disk
    S --> W
    W --> F
    F --> B
    B --> R
    R --> D

One chunk saved, across the three lanes that touch it, coloured by which one: the server’s share for a full chunk is the first box alone, and it ends at a snapshot, so everything to the right of it happens after the tick that decided to save has moved on. The two middle boxes are the same lane at two priorities — the foreground task waits for the encode and parks the tag in IOWorker.pendingWrites, and the background one runs only when nothing is queued in front of it, which is why a write can sit there for a long time and cost the Server thread nothing.

That figure is the page’s answer to why doesn’t saving lag the server. Apart from flushing the chunk’s POI column and, for a chunk that is not full, the guard’s read, the Server thread’s whole share of a save is the middle of ChunkMap.save: SerializableChunkData.copyOf, which copies every LevelChunkSection with LevelChunkSection.copy and each non-empty block and sky DataLayer out of LevelLightEngine.getLayerListener, clones every heightmap the chunk holds, pulls block-entity NBT through ChunkAccess.getBlockEntityNbtForSaving, packs the ticks through ChunkAccess.getTicksForSerialization, and packs the structure starts. Everything after that — the palette codecs, the deflate, the sector arithmetic, the syscall — runs somewhere else, and ChunkMap.save returns as soon as the copy is done.

Four folders, three of them the same shape

LevelStorageSource.LevelStorageAccess.getDimensionPath defers to DimensionType.getStorageFolder, which puts every dimension — the overworld included — under dimensions/<namespace>/<path>/ in the world folder. Inside are region/, entities/, poi/ and data/. The first three are region stores of the same shape — a folder of r.X.Z.mca files, a RegionFileStorage, an IOWorker, and a RegionStorageInfo naming the store (chunk, entities or poi) so that MinecraftServer.reportChunkSaveFailure can say which one broke. Only chunk belongs to ChunkMap itself; EntityStorage and SectionStorage each hold a SimpleRegionStorage rather than being one. data/ is not a region store at all, and it is the section below.

A LevelChunk’s entities are not in region/. The SerializableChunkData.entities list is written only when the chunk’s persisted status is a ChunkType.PROTOCHUNK — worldgen’s spawns, waiting for the column to become full. A full chunk’s entities live in entities/, one region entry per chunk, holding a Position and an Entities list. If an old save still has entities inside a full chunk’s region/ entry, ServerLevel.addLegacyChunkEntities adopts them on load.

The other store under data/

SavedDataStorage is the fourth folder and the one that is not a region store: no sectors, no LRU of open files, one gzipped data/<namespace>/<path>.dat per thing that has state. It keeps the Server thread off the disk as a chunk does, and for the same reason. SavedDataStorage.scheduleSave encodes every dirty entry on the caller’s thread — the Server thread, inside the save it was asked for — and hands the finished tags to Util.ioPool, at most Util.maxAllowedExecutorThreads writes at a time, chaining each onto SavedDataStorage.pendingWriteFuture so that two saves of one file cannot race. SavedDataStorage.saveAndJoin is the only place anything waits, and only a flush save, shutdown or the world upgrader reaches it. Encode while the world is still, write while it moves: half the bargain the chunk path makes, over a much smaller object. Which file holds what belongs to level data and rules.

The four moments a chunk is written

the momentwhat runs itwhich chunkswhat holds it back
an unloadthe task ChunkMap.scheduleUnload queued, drained by ChunkMap.processUnloadsthe one chunk being dropped, at whatever status it reachedthe tick’s budget, except for the oldest tasks past 2,000 in ChunkMap.unloadQueue, which drain regardless; no cooldown
the eager sweepChunkMap.saveChunksEagerly, the last statement of that same ChunkMap.processUnloadseverything in ChunkMap.chunksToEagerlySave20 a tick, fewer than 128 writes outstanding, the tick’s time budget, and ten seconds per chunk
an autosaveMinecraftServer.autoSave → ServerLevel.save → ServerChunkCache.save → ChunkMap.saveAllChunks, without flushevery holder in ChunkMap.visibleChunkMaponly the per-chunk gates: ChunkMap.saveAllChunks clears ChunkMap.nextChunkSaveTime, but ChunkMap.saveChunkIfNeeded still wants an accessible, ready, unsaved LevelChunk (an ImposterProtoChunk passes its type test but is never unsaved)
a flush save/save-all flush, /stop, ServerChunkCache.closeevery accessible holder, over and over until a pass saves noneit blocks the Server thread instead

The dirty set behind the second row is narrower than it looks. ChunkMap.setChunkUnsaved is installed as WorldGenContext.unsavedListener and handed to a chunk by ChunkStatusTasks at the moment it becomes full, and LevelChunk.markUnsaved fires that listener only on the false→true edge. So ChunkMap.chunksToEagerlySave holds full chunks that have changed since their last write, each added once, and a chunk still being generated is never in it.

Turning saving off is not as total as it sounds. ChunkMap.tick ticks PoiManager first and unconditionally, and only then asks ServerLevel.noSave whether to run ChunkMap.processUnloads — so a no-save world still writes village data through SectionStorage.tick, and stops letting go of chunks until something forces the issue. The only other drain of ChunkMap.unloadQueue and ChunkMap.toDrop is inside ChunkMap.saveAllChunks with flush, which runs ChunkMap.processUnloads on an always-true budget — so /save-all flush unloads them, since MinecraftServer.saveAllChunks suppresses ServerLevel.noSave when force is set. Shutdown needs neither: it clears ServerLevel.noSave on every level, which reopens the tick’s drain, and ticks until ChunkMap.hasWork is false before its flush save. An explicit save is a different question again: MinecraftServer.saveAllChunks passes ServerLevel.noSave on to ServerLevel.save only when its force flag is clear, and /save-all sets that flag while MinecraftServer.autoSave does not.

The guard against writing a half-made chunk over a finished one, and where it leaks

The cast row promises this one, so here it is: ChunkMap.save refuses to write a non-full chunk over a full one on disk, and the refusal is best effort in two ways. ChunkMap.isExistingChunkFull returns false — meaning go ahead — whenever the read throws or comes back empty, so an IO error licenses exactly the clobber the guard exists to prevent. And ChunkAccess.tryMarkSaved clears the unsaved flag before the guards run, so a chunk the guard turns away has already been marked clean and will not be offered again.

Two shapes never reach the write at all. A proto chunk still at ChunkStatus.EMPTY with no valid structure start is dropped by the same block, and an ImposterProtoChunk refuses outright: ImposterProtoChunk.tryMarkSaved and ImposterProtoChunk.canBeSerialized are flat falses (so is ImposterProtoChunk.isUnsaved), where ImposterProtoChunk.markUnsaved, ImposterProtoChunk.isLightCorrect and ImposterProtoChunk.setLightCorrect defer to the chunk underneath (chunk anatomy). The imposter’s reason is that the LevelChunk under it is what the saver will be handed instead.

A chunk nobody needs any more

The save above is the middle of a longer story, which ordinarily runs inside one tick and stretches over several only when the budget runs out or a save dependency is pending. This is the whole of it, from the level change that drops the chunk to the write that finally records it.

sequenceDiagram
    participant DM as DistanceManager
    participant CM as ChunkMap
    participant CH as ChunkHolder
    participant SCD as SerializableChunkData
    participant IOW as IOWorker
    participant SL as ServerLevel
    participant PESM as PersistentEntity<br/>SectionManager

    DM->>CM: the level climbs past ChunkLevel.MAX_LEVEL, so the key joins ChunkMap.toDrop
    Note over CM,CH: the unload phase of the same ServerChunkCache.tick
    CM->>CM: processUnloads moves<br/>the holder to<br/>ChunkMap.<br/>pendingUnloads
    CM->>CH: getSaveSyncFuture, and the unload task is hung off it
    CH-->>CM: the future is complete, or completes later, and the task joins unloadQueue
    Note over CM,CH: the same drain while the budget says yes, or a later one, or among the oldest past 2,000
    CM->>CH: getSaveSyncFuture again — the same one, or the task rearms and stops
    CM->>CM: this exact holder<br/>leaves ChunkMap.<br/>pendingUnloads, or<br/>a ticket took it back
    CM->>CM: setLoaded false, then save — PoiManager.flush and the proto-over-full guard
    CM->>SCD: copyOf takes the snapshot, a worker turns it into a CompoundTag
    CM->>IOW: store, handed the encode future, on the chunk lane
    CM->>SL: unload clears the block entities and the tick containers
    Note over SL,PESM: in a walk-away, the entities left rings earlier, when the chunk fell below FULL
    IOW-->>CM: the IOWorker.PendingStore future completes, one fewer active write

A chunk nobody needs, from the level that stopped needing it to the write that records it, ordinarily inside one tick: the two note bars mark where the budget or a pending save can push the rest to a later one, and the two dotted arrows are completions the Server thread does not wait for. The entities are not in it — in a walk-away they left by a different road, earlier, when the chunk fell below full.

Three things there are load-bearing. The first is that nothing happens until ChunkHolder.saveSync is done: every promotion future is chained into it by ChunkHolder.addSaveDependency, and so is GenerationChunkHolder.generationSaveSyncFuture for as long as a generation step holds a reference, so a chunk mid-promotion or mid-generation cannot be saved or unloaded at all.

The second is the guard. ChunkMap.pendingUnloads is removed by identity: if a ticket re-adopted the position while the task waited, ChunkMap.updateChunkScheduling has already pulled the holder back out of that map, the removal fails, and the task quietly does nothing — nothing is lost and nothing is written twice. And if the sync future changed while waiting, the task rearms itself on the new one rather than proceeding.

The third is that entities go by a different road, and earlier. PersistentEntitySectionManager.updateChunkStatus saw the chunk fall below full, rings before it was dropped, and queued the position in PersistentEntitySectionManager.chunksToUnload. If the chunk’s entity file is still being read, PersistentEntitySectionManager.storeChunkSections returns false and the whole thing is retried next tick, so a half-loaded set never clobbers the file. Otherwise each entity EntityAccess.shouldBeSaved accepts is serialised with Entity.save on the Server thread, its passengers inside it, the tag goes to the entities lane, and those entities and their passengers are removed with Entity.RemovalReason.UNLOADED_TO_CHUNK. The three types besides Player built never to be saved — a leash knot, a lightning bolt, a fishing bobber — pass the filter, write nothing in Entity.save, and are removed with the rest. The filter runs before the removal, not after it, so what it turns away on its own account — a Player, a vehicle carrying exactly one player — is neither written nor removed. The last of those is not an oddity: ServerPlayer writes its own root vehicle into the player file under RootVehicle under exactly that condition, so a boat with one rider travels in the player’s file rather than the chunk’s.

Two other things leave with the chunk, both after the snapshot is taken: ServerLevel.unload clears its block entities and unregisters its tick containers, and ThreadedLevelLightEngine.updateChunkStatus queues the light engine to forget its layers (lit before you ever see it).

Why the server thread never waits

IOWorker is not a thread. It holds a PriorityConsecutiveExecutor over Util.ioPool — a cached pool whose threads are named IO-Worker-n — and its guarantee is that one task at a time runs for that store, not that the same thread runs them. Its three priorities are strictly ordered: IOWorker.Priority.FOREGROUND for IOWorker.store and IOWorker.loadAsync, IOWorker.Priority.BACKGROUND for IOWorker.storePendingChunk — the task that touches the disk — and IOWorker.Priority.SHUTDOWN last. The lowest priority has exactly one user in the whole game: the barrier IOWorker.waitForShutdown parks behind everything else when the store closes. A flush is not one of them — IOWorker.synchronize submits it at foreground priority like a store — but it still lands behind the writes, because before it flushes it waits on every IOWorker.PendingStore future, and those complete only when the background tasks have run.

IOWorker.pendingWrites is what that buys. It is a sequenced map from ChunkPos to IOWorker.PendingStore, and a second store for a position already in it overwrites that entry’s data in place without moving it, so N saves of one chunk before the lane drains become one disk write and one shared future. IOWorker.loadAsync looks in the same map first and returns a copy of the pending tag, so a chunk re-loaded while its write is still pending never touches the region file — read-your-writes by lane order rather than by any lock. IOWorker.STORE_EMPTY is the null supplier that means delete, and IOWorker.scanChunk is the streaming ChunkScanAccess that StructureCheck uses to peek into chunks nobody has loaded.

The three places that do wait

Three places besides a synchronous chunk load and a flush save make the Server thread wait on a disk. ChunkMap.isExistingChunkFull, the guard that stops a ProtoChunk overwriting a finished chunk, answers from ChunkMap.chunkTypeCache when it can but joins the read future inline on a cold entry — the IO lane, then a datafix pass on the worker pool. And SectionStorage.getOrLoad joins too, for a POI section that SectionStorage.prefetch has not fetched. The third is not a chunk-storage method at all: StructureCheck.tryLoadFromStorage joins IOWorker.scanChunk to peek at a chunk it will not load, which is what an eye of ender, a dolphin, an explorer map and /locate all end up doing on the Server thread. Only the first is on the save path, and only for a chunk that is not full, which is why a full chunk’s save costs a copy.

Inside a region file

The last box of the first figure is a file format, and it is where the ordering that makes a half-finished save survivable lives.

flowchart TD
    A["IOWorker.storePendingChunk pops the oldest entry of IOWorker.pendingWrites"] --> B["RegionFileStorage.getRegionFile, an LRU of 256 regions"]
    B --> C["RegionFile.getChunkDataOutputStream wraps a RegionFile.ChunkBuffer in the compressor, NbtIo writes in"]
    C --> D["closing the buffer back-patches the length and calls RegionFile.write"]
    D --> E{"how many sectors"}
    E -- "under 256 sectors" --> F1["RegionBitmap.allocate takes the first free run"]
    F1 --> F2["the compressed chunk goes to those new sectors — the content, first"]
    F2 --> F3["offsets and timestamps updated, then RegionFile.writeHeader"]
    F3 --> F4["any stale sidecar for this chunk is deleted"]
    F4 --> Z["and only now are the old sectors freed"]
    E -- "256 sectors or more" --> G1["one sector is allocated for a stub"]
    G1 --> G2["a five-byte EXTERNAL_STREAM_FLAG stub, and the payload to a temp file"]
    G2 --> G3["offsets and timestamps updated, then RegionFile.writeHeader"]
    G3 --> G4["the temp file is moved onto c.X.Z.mcc — the content, last"]
    G4 --> Z

Two ways to write one chunk, putting the content and the pointer to it in opposite orders: on the left the bytes are on disk before the header that names them, and on the right the header is written first, pointing at a stub, and the real payload only lands when the temp file is moved. Both arms meet at the last box, which is the rule that makes either safe: the old sectors are not freed until the new ones can be found.

A RegionFile is one r.X.Z.mca: two header sectors (RegionFile.SECTOR_BYTES is 4096) holding a 1024-entry offset table, RegionFile.offsets, packed as sector number ≪ 8 with the sector count in the low byte, and a 1024-entry RegionFile.timestamps. Free space is a RegionBitmap, with the header’s two sectors forced used at construction and RegionBitmap.allocate handing out the first run big enough. Nothing ever reads the timestamp table back: RegionFile.write stamps each entry with epoch seconds from RegionFile.getTimestamp, and the only clock that spaces one chunk’s writes is the monotonic Util.getMillis behind ChunkMap.nextChunkSaveTime, while the autosave counts server ticks. None of them is game time, which the chunk carries only as data. Each stored chunk starts with RegionFile.CHUNK_HEADER_SIZE (5) bytes — a length and a compression id — and both RegionFile.write and RegionFile.getChunkDataInputStream are synchronised on the RegionFile itself rather than on the channel, though in practice only one lane ever drives a given folder.

Read the two branches of the figure against each other and the page’s best fact falls out. For an ordinary chunk the new bytes are on disk before the header points at them, and the old bytes are released after — so a crash at any point leaves either the old chunk or the new one, and a chunk never overwrites itself in place. For an oversized chunk the ordering is reversed. Anything needing RegionFile.EXTERNAL_CHUNK_THRESHOLD (256) sectors or more cannot be described by an eight-bit count field at all, so it goes to a .mcc sidecar and the region file keeps only a stub carrying RegionFile.EXTERNAL_STREAM_FLAG; and the sidecar is moved into place after RegionFile.writeHeader has already committed the pointer to it, destroying the previous copy at a fixed path. The in-file case is content-then-pointer. The sidecar case is pointer-then-content. Either way the ordering only buys anything if the writes reach the platter in that order, which is what DSYNC is for: MinecraftServer.forceSynchronousWrites returns true as the base default, and two of the three servers override it — DedicatedServer from DedicatedServerProperties.syncChunkWrites (sync-chunk-writes, default true) and IntegratedServer from Options.syncWrites, whose default is true only on Windows. GameTestServer keeps the base answer.

The compression byte is per chunk, not per file. RegionFileVersion.selected — set once by RegionFileVersion.configure from DedicatedServerProperties.regionFileComression (Mojang’s spelling; the property is region-file-compression) — decides only what new writes use, choosing between RegionFileVersion.VERSION_DEFLATE (the RegionFileVersion.DEFAULT), RegionFileVersion.VERSION_NONE and RegionFileVersion.VERSION_LZ4. Reads honour whatever byte each chunk carries, including RegionFileVersion.VERSION_GZIP, which has no option name and so can be read but never chosen, and RegionFileVersion.VERSION_CUSTOM, which exists so that RegionFile.createChunkInputStream can recognise it and refuse.

The way back in

Loading is the same road driven backwards, in four stages across three lanes. ChunkMap.scheduleChunkLoad starts with IOWorker.loadAsync on the IO lane; ChunkMap.readChunk then hops to Util.backgroundExecutor under the name upgradeChunk for ChunkMap.upgradeChunkTag, which is where datafixing happens; SerializableChunkData.parse runs on the same pool under parseChunk, so those two stages share a lane; and SerializableChunkData.read runs on the Server thread, where the sections are installed, the saved light is queued into the light engine, and PoiManager.checkConsistencyWithBlocks re-derives the points of interest of any section whose stored record is missing or not marked valid. Running beside all of it, SectionStorage.prefetch pulls the POI file in, and the two are joined before the server-thread step — which is exactly why that step’s SectionStorage.getOrLoad calls do not block. From there the generation pipeline takes over — including what happens when the bytes will not parse.

Entities come back the same shape but land differently: EntityStorage schedules both the datafix and EntityType.loadEntitiesRecursive on EntityStorage.entityDeserializerQueue, a ConsecutiveExecutor over the server main-thread executor, so only the NBT read is off-thread.

Doing all of it at once, with no server running

Optimize World in the world-select screen is the same read and the same write with the game in between removed. WorldUpgrader starts a single daemon thread, World Upgrader #n, and hands each of the three stores to a RegionStorageUpgrader, which walks every r.X.Z.mca file in the folder, datafixes each chunk tag and writes back the ones that changed — or every one, into fresh region files, which is what compacts a save whose sectors have fragmented, though only the dedicated server’s –recreateRegionFiles asks for that and the button never does. UpgradeProgress is the counter the screen reads. Nothing here loads a chunk, generates one, or consults a status: the world is a folder of tags, and the button’s whole promise is that every tag is at the current data version before a server ever opens the save.

Questions players ask

Does the game stall when it saves? The chunk stores wait for the disk only on a flush, though an autosave still copies every unsaved chunk, serialises the loaded entities, and writes level.dat and every player’s file itself, on the Server thread, in the tick it falls. ChunkMap.saveAllChunks with flush loops over the accessible holders, blocking the main-thread executor on each ChunkHolder.isReadyForSaving until a whole pass saves nothing, then flushes POIs with SectionStorage.flushAll, runs ChunkMap.processUnloads with an always-true budget, and finally joins IOWorker.synchronize with flush. That, with the entity store’s and the saved data’s own joins in the same save, is where waiting for the disk is the point rather than an accident, and it is what /save-all flush and /stop do.

Does lowering the tick rate push out my autosave? Only the one already counting down, and never past five minutes. The interval is counted in ticks: MinecraftServer.computeNextAutosaveInterval is the tick rate times 300 — or, while the server is sprinting, 300 times the rate its recent tick times imply — floored at MinecraftServer.MIMINUM_AUTOSAVE_TICKS (100, and the typo is Mojang’s), and the very first is MinecraftServer.AUTOSAVE_INTERVAL (6000 ticks), so each autosave is five minutes of the server’s scheduled time at any rate. MinecraftServer.onTickRateChanged recomputes it on every /tick rate but assigns it only when it is smaller than the ticks left: a slower rate stretches those ticks, up to five minutes from the change, and a faster one can only bring the save forward. The server tick has the rest of that loop.

Why is my entities/ folder full of files with nothing in them? Because nothing deletes a region file once its entries are cleared — and emptying a chunk costs one write. EntityStorage.storeEntities with an empty set only writes when EntityStorage.emptyChunks did not already contain the position, and that write is IOWorker.STORE_EMPTY, which zeroes the region entry and deletes any sidecar. The first time a chunk goes empty costs a write. Every later save of it costs nothing.

Where to look

ChunkMap.tick · ChunkMap.processUnloads · ChunkMap.scheduleUnload · ChunkMap.save · ChunkMap.saveChunksEagerly · ChunkMap.saveChunkIfNeeded · ChunkMap.saveAllChunks · SerializableChunkData.copyOf · SerializableChunkData.write · IOWorker.store · IOWorker.storePendingChunk · IOWorker.loadAsync · RegionFileStorage.write · RegionFile.write · RegionBitmap.allocate · PersistentEntitySectionManager.storeChunkSections · EntityStorage.storeEntities · SectionStorage.writeChunk · SectionStorage.prefetch · ChunkMap.scheduleChunkLoad · SerializableChunkData.read · MinecraftServer.computeNextAutosaveInterval · DimensionType.getStorageFolder

Next door: tickets and loading raises the level, chunk anatomy owns what LevelChunkSection.copy copies, lighting owns the layers the unload throws away, points of interest owns the poi/ store, the server tick owns the budget every method here is handed, how a server dies is the save that does not happen, and entity lifecycle is what Entity.RemovalReason.UNLOADED_TO_CHUNK means to a mob.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.