Every category of the system keeps a register of its own limitations, and a row leaves it only when the test named in that row goes green. Open means the work has not been done. Retiring means the mechanism is built and tested and the row is waiting on something that is not code — a live run across the open internet, or a credential that cannot live in a public repository. There are 10 registers.
Random ticks suppressed in delegated regions (no grass, fire, or crop ticking there). Engine-owned random ticks landed: vanilla-shaped selection with state-derived eligibility that skips without consuming randomness, canonical column order, grass MVP semantics, and a 200-tick 3-replica identical-root soak with the lane actively spreading. Crops landed (`CropGrowthTest`, 6): farmland plus wheat ages 0–7, growth gated on farmland below and light 9 at the crop's own cell, one stage per selection with the draw taken before any condition is read so the consumed randomness never depends on the surroundings, and only seeds placeable — a grown crop is an engine output, so a harvest cannot be minted. Fire landed with L-3. The live suppression mixin landed: `ServerLevelRandomTickMixin` cancels the whole chunk pass in a delegated region before vanilla consumes the level RNG (a per-block filter would already have drawn), counted separately from the scheduled-tick counter so a farm soak reads its own number. Remaining: the farm soak itself, with that counter read at the end — and it is gated on the same seat problem as minecraft L-80, because the scripted suites run a dedicated server that the field-of-view planner leaves holding no regions, so nothing is suppressed there and the counter is honestly zero
Closes when: Farm soak: engine-owned random ticks, identical roots on 3 replicas, suppression counter deleted
RETIRING
Fluids excluded from the validated lane. Finite deterministic fluids landed: a per-cell automaton on the hashed scheduled-tick queue, level encoded in the block id, pure-neighbourhood desired state (fall first; horizontal contribution requires the neighbour to sit on solid; support loss drains the network), vanilla cadence and reach, deterministic water-over-lava, `BorderSignal.Kind.FLUID` with no halo writes. Fluid interactions landed (`FluidInteractionTest`, 6): a lava source water reaches becomes obsidian, a lava flow becomes cobblestone, and lava arriving above water becomes stone — water is never consumed, which is both vanilla's rule and what keeps the outcome a pure function of the neighbourhood. `RULES_VERSION` 4→5, palette literal `palette.v5`. Producer and consumer landed (2026-07-28). The gap was never the automaton: `RegionHalo` could always answer a border read from neighbour columns and `FluidRules.seedBorderInflow` already re-evaluates the owned cells a halo fluid borders — but `HaloUpdate` (tag 56) had ZERO producers and ZERO consumers, so every halo on the network was empty and the receiving region correctly concluded there was nothing beyond the boundary. It now has both. `HaloSlicer` cuts a committed snapshot's edge columns for one neighbour (selection asks the RECEIVER's ring, so a sender cannot widen its own contribution); `HaloStore` holds the newest slice per source, newest-wins so delivery order cannot decide what a region reads; `WorkerValidationService` emits `HaloUpdate` to each neighbour committee from `activateRegion` and `commitLocally`, consumes it in `onHaloUpdate`, hands the assembled halo to `requestFor` and to the fallback lane, and forgets it on revoke. Proven end to end by `HaloExchangeIT` (two workers, real encoded frames over a transport: with an empty halo the far bank stays air, and after the neighbour's edge arrives the receiving region's own committee round floods it with water) and by `HaloExchangeTest`. A second, independent defect surfaced the moment fluids could finally flow through a commit: `WorldMutationApplier`'s duplicate check was still at SECTION granularity — a leftover of the retired section-paint delta model — so every certified delta touching two blocks of the same 16³ box, which is every spreading fluid, aborted with `DUPLICATE_BLOCK_TARGET`. The conflict unit is the exact position now; duplicate POSITIONS are still refused. The halo is a consensus input now (2026-07-28, second pass). Both halves the first pass named as missing landed together, and the committee-of-one gate is gone. (1) Provenance: every member of a region's committee signs the edge slice it cuts — `HaloEndorsement` (type tag 119: signer, source region, epoch, snapshot version, canonical slice root), carried by `HaloUpdate` body version 2. A receiver recomputes the slice root from the bytes that actually arrived, checks the anchor against the source lease it holds, requires the signer to be a member of that committee and the signature to verify against that member's registered key, and applies the slice only once a STRICT MAJORITY of the source committee has signed the identical root — so a single lying member, or a courier swapping columns under an honest signature, cannot define what a region reads across its border, and two receivers holding a quorum-backed slice necessarily hold identical columns. The verifying receiver needs the neighbour's lease, which is the same neighbour-committee knowledge the cross-region entity-transfer lane already depends on. (2) Agreement on inputs: `RegionProposal` body version 4 names the exact `(source, version)` halo pins the proposal was executed against, inside the signed portion, and a validator executes against those pinned slices rather than its own accumulated view — holding one slice more than the primary is no longer a divergence, and a validator that does not hold a pinned slice at the pinned version declines the round cleanly instead of computing a different root. Proven by `HaloExchangeIT`'s multi-member case: ORIGIN and EAST each held by a committee of TWO, one ORIGIN member's endorsement leaves EAST's border shut, the second opens it, both EAST members then reach quorum on a round that floods the far bank with water, with zero divergences on either member and a two-vote certificate. Verified load-bearing: making the validator ignore the pins, and separately restoring the committee-of-one gate, each fail that test; both restored green. Remaining: the exit test also names LAVA and live evidence — the multi-member proof above is water, headless; a cross-boundary lava case and a live run are what is left
Closes when: Water and lava spread deterministically across replicas, including cross-region via the migration lane
RETIRING
Mobs are server-authoritative ghosts; AI is not validated. Deterministic ghost-mob AI landed: ghost zombies move from the validated root, one decision per 10 ticks in canonical id order with a fixed draw count regardless of branch, steps only onto walkable cells, borders fail closed, despawn horizon enforced every tick. A 2400-tick soak ends with every ghost on a walkable cell and replica-identical roots. Remaining: targeting, pathfinding, combat, per-species retirement and the mirroring default flip (L-24), live evidence
Closes when: Per-species retirement: ghost share = 0 for every shipped species in soak
RETIRING
Player movement is server-authoritative. Engine core landed: `MovePlayerAction` (tag 106) — the client proposes, the committee decides legality (per-axis ≤ 1 block/action, destination passability at feet and head), and a border step becomes the dupe-proof cross-region transfer carrying the whole player payload. Teleports, speed hacks, and wall clips die in validation on every honest replica. Remaining: the client prediction/rollback overlay (rides L-16), mod-side capture of vanilla movement into `MovePlayerAction`s, live evidence
Closes when: Optimistic validated movement with rollback; cheat movement rejected by the committee
RETIRING
Committed effects appear 1–2 ticks late (ghost/pending render). Commit-feed wiring landed: `WorkerValidationService.onCommit` streams every committed snapshot and root to one observer, and `ClientValidationLane` constructs a `LocalReplicaView` and feeds it on every applied commit. Remaining: the renderer bind to `render()` and the capture-point `predict()` calls — the GUI half. The overlay is now fed: `LocalReplicaView` has had prediction and reconciliation since Task 16a and *nothing ever called `predict`* — the only thing that advanced the view was a commit coming back from the committee, which is exactly the one-to-two-tick lag this row describes from a player's chair. `PredictionFeed` (peer, 6 tests) sits between the capture path and the view — a rendering concern stays out of the submit path and a Minecraft concern stays out of the engine — and `LiveEntityLaneRuntime` offers every signed action to it — on both the owned path and (since the L-17 retirement) the observer path, which is the common one under field-of-view ownership. Four answers are all ordinary and none is an error: no view (every dedicated server), an untracked region, an action the engine refuses, and a view that throws (prediction is latency-hiding, so a fault costs a late-looking block and never the submit path). Remaining: the renderer never reads `render()` — the GUI bind, which rides L-46
Closes when: Client prediction + rollback: local apply instant, reconciliation invisible in normal play
OPEN
Genesis is still a single-signer trust root, and L-20 was retired on code nothing calls. `CertifiedWorldGenesis` — the record production actually mints, persists and reuses — carries `authorNodeId` + `authorPublicKey` + one `signature`, and no founding set at all. The multi-party upgrade L-20 claimed (`GenesisRecertification` tag 104, `GenesisApprovalFlow`, `GenesisApprovalRequest`/`Grant`) is complete and tested and has zero non-test references: `:peer:structureReport` lists `GenesisApprovalFlow` under "classes only tests and benchmarks reference", and a grep confirms `GenesisApprovalFlowIT` is its only construction site. So every world on the network is founded on exactly the trust root L-20 describes as the limitation. This is not a regression of the mechanism — the mechanism was never reachable — so the retired row's evidence is left untouched as history and this row carries the live claim. The blocker is a design decision, not a call site: recertification needs a *declared founding set*, and nothing in the shipped state has one. Who counts as a founder (the author plus the first N joiners? everyone present when the world is first shared? an explicitly nominated set?) determines what the quorum means, whether a late joiner can dilute it, and what a founder's key is checked against — and it has to be persisted with the world, because a set derived at verification time is whatever the verifier's session happens to contain
Closes when: A world minted by the production path carries a founding set, and a peer joining it verifies a `GenesisRecertification` whose approvals reach the declared set's strict majority — asserted through a production call path, i.e. a test that fails when the flow's construction site is removed, not only `GenesisApprovalFlowIT`
OPEN
A mod refused by the async write guard is pointed at a door with nothing behind it. `MutationGuard.verdictChecked` throws `AsyncWriteException` naming `AsyncActionGate.submit` as the legal path, and the mod-side `BlockWriteGuard` repeats that name in its own message — but `AsyncActionGate` has zero production references (`:peer:structureReport` §2.2, grep-confirmed 2026-07-30: the only mentions are those two exception strings and its own tests). Nothing constructs the gate, nothing drains its FIFO, and no tick-time caller applies what it holds. The *safety* half of L-25 is genuinely wired and unaffected — `LevelChunkMixin` routes every block write through the guard and an off-thread write is still rejected — so this is a completeness gap rather than a regression: a third-party mod that does exactly what the error message tells it to do has no working path at all, which makes the `SDK.md` contract unimplementable by anyone outside this repository
Closes when: A rule pack submits a signed action from a non-main thread through `AsyncActionGate.submit` and observes it applied at the next tick drain, with the drain reached from a production call path — a test that fails when the drain's call site is removed
OPEN
The entity lane's live activation self-bootstraps and is proven live (entity lane on 12 regions with the P2P mesh formed; 239 validated/ghost entities across 12 delegated regions with versions advancing; reopen resumes from the store head; clean-slate pickup delivered exactly once). Headless exits are green: jqwik 3 replicas, disjoint committees, forced-process paired-log `@Invariant(11)`, bootstrap plan determinism, dirty-shutdown compensation, and 23,040 B/mob/min at 0 resync bps. Remaining: the certified genesis-from-existing-world replacing the interim manifest end to end, and the scripted pickup/zombie/pearl gameplay drives running in CI
Closes when: Reopen resumes from the store head; ghost captures refresh expected state from canonical; validated pickup credit lands exactly once on the vanish repro; the certified extractor replaces coarse digests; per-joiner identities in the plan; pearl ghost/materialize/teleport drives
RETIRING
There is no automated installer-based, cross-machine acceptance. The halves that exist are green in CI: a companion job builds the app end to end, runs the gate both ways (worker present ⇒ Minecraft starts; absent ⇒ an actionable abort), and proves hosted-world survival with a real daemon and a SIGKILLed stand-in game process. What is missing is the sentence a player would actually say — install the app, host a world, close Minecraft, and have it still be listed and joinable from a second, separately networked side
Closes when: A CI job installs the app, verifies the gate both ways, hosts a world, closes Minecraft, and joins that world from a second machine or separately networked instance
OPEN
Two connection settings can never be honoured as specified. Settings → Network exposes a per-world connection cap and an unlimited-connections-only filter. Neither is implementable against the architecture as it stands, and both are kept in the UI on purpose, permanently badged with the worker's own reason rather than deleted — removing them would silently drop settings users already saved and would hide that the limitation is known. *Per-world caps:* the socket transport has no world dimension at all — a socket is not owned by a world — so making it real needs either a world dimension on the transport or a world→manifest accounting index, i.e. an architectural change rather than a settings change. *Unlimited-connections-only:* no peer advertises a connection cap anywhere on the wire, so there is nothing to filter peers on; it would need a new field in the frozen membership family, mirrored in the Rust codec. The worker returns both as `rejected` with these reasons and the app renders a distinct muted "not supported" badge — deliberately not the amber "not enforced yet", which would imply the feature is coming
Closes when: Either the transport gains per-world attribution and a peer-advertised cap field, or both controls are removed from the UI with their saved values migrated
OPEN
The direct launch route cannot sign in. Play assembles a full Minecraft command line — version-chain resolution through `inheritsFrom`, classpath ordering, natives extraction, the complete placeholder table, a probed Java runtime, `--quickPlayMultiplayer` — but starting the game needs a complete Microsoft account token/profile. A client-id environment variable alone is explicitly insufficient and never enables Direct. No approved credential/token flow exists in this repository, so planning chooses Prism, `servers.dat`, or address before opening a tunnel rather than failing during sign-in
Closes when: An approved Microsoft flow yields a complete online account; Direct spawns the JVM and opens the world with no other launcher installed
OPEN
The offline account is test-only. `launch::auth::Account::offline` fills `${auth_*}` placeholders so command assembly can be exercised without login, but production planning never treats it as usable authentication. A LAN-opened vanilla world authenticates joiners against Mojang and would refuse it
Closes when: Either L-91 supplies a real account and offline remains unreachable outside tests, or offline-mode targets are supported end to end and documented
OPEN
The `servers.dat` route is not auto-connect. For a player using the official launcher, Play writes the tunnel address into their Multiplayer list and opens the launcher: one click from the world rather than none. The official launcher has no command-line way to join a server, so this is the ceiling for that launcher, and the interface says so in those words rather than implying the game will open in the world. Players with Prism or MultiMC get the real thing — `--server` becomes `--quickPlayMultiplayer` and the game lands in the world
Closes when: Either the official launcher gains a join argument, or L-91 is resolved and these players get the direct route
OPEN
The rebuilt native Android settings need complete physical touch acceptance. Android 15 proves startup, bar navigation, Peers identity/self-test, Privacy rendering, and warm-start deep-link routing. Host gates now cover the unified tracker/store page, four-step onboarding, typed field mutations, verified storage choices and visible battery optimization, but those final interaction/result paths have not run on the reconnected phone
Closes when: Network and Storage are edited on a phone; a tracker-store deep link is previewed, added and removed; process restart preserves values; the hardened 130-second E2E passes
RETIRING — host build and earlier physical smoke green;
The rest of this row
mutation/restart plus hardened E2E pending
A custom appearance applies to the desktop window only. The phone renders in Compose against the system palette and cannot read CSS, so a theme authored here has no effect there. The value is stored, never dropped, and the screen says so on its face rather than implying a preference that travels
Closes when: The Android companion reads `appearance.themes` and renders at least the accent and surface roles from the selected appearance, or the register records that it structurally cannot
OPEN
A stored appearance whose CSS the parser rejects is dropped silently at startup. Nothing is overwritten and the base scheme renders correctly, but the reason is only shown when the Theme screen is opened — so a theme that stopped working after a webview upgrade looks like a theme that was never saved
Closes when: Applying a stored appearance whose CSS fails to parse raises the same fault channel the settings document uses, so the reason is visible without opening the editor
OPEN
An arbitrary shared-storage folder may be picked but unusable. Android 11+ withholds raw `File` access to shared storage regardless of the SAF grant, and the worker writes with `java.io.File`. The app detects and reports this (`NoderaStorage.kt:171-191` probes with a real write); it cannot remove it without a storage layer that speaks `content://`.
Closes when: A folder outside app-specific storage is picked and the worker writes a world archive into it
OPEN
One ABI per APK. The build targets `aarch64`; `--target` switches it, but nothing produces a universal APK or an App Bundle.
Closes when: One artifact installs on arm64 and armv7 devices
OPEN
The LAN tunnel lane is untested on Android. The worker joins the multicast group on the phone (`Watching 224.0.2.60:4445`), but no test drives a real game through it from a handset.
Closes when: An unmodified Minecraft client reaches a LAN world through a phone-hosted tunnel
OPEN
M-NET-2retiringfrontend16 The app-to-worker P2P port path has no physical-phone exit evidence yet. Mobile Settings exposes random/fixed P2P ports; Rust writes the validated values to `nodera-worker.properties`; a context/setup gate starts Kotlin only after the handoff; Java reads `NODERA_P2P_PORT` environment-first/property-second. Control is intentionally excluded so Android's Rust client and Java worker agree on `25610`. `AndroidPortPropertyTest` proves a real worker reports the requested property port in `NODERA-STATE.self_route`; five app tests cover parity, control isolation, replacement and both startup orders. A signed debug APK builds, but no physical phone selected a one-port range and passed the exact state assertion.
Closes when: The phone binds a P2P port chosen in settings
RETIRING — headless proof green;
The rest of this row
physical state proof pending
No client multiplayer GUI beyond server-pushed surfaces. The screens are built: a tabbed Nodera-only multiplayer screen with a scrollable world list, search, health rows, status footer, live tracker and rendezvous tabs, a create-world torrent toggle with an independent password field, and an end-to-end join flow. What remains is the live pass with real tracker data in a GUI environment
Closes when: The multiplayer page lists torrent worlds with per-world player/chunk/reliability counters, red/gray health and a 24 h countdown; create-world offers torrent hosting with a password; verified in a real client
RETIRING
The GUI redesign is proven by view-model tests and compile-clean screens, but the presentation is unverified live: "Open to Nodera" occupying the vanilla LAN slot, the single-player public-world badge's per-row placement (screen-level summary only until the world-list mixin lands), the tabbed multiplayer screen, and the piece-map grid filling as pieces arrive. All four data feeds are live-wired; what is unverified is what a player sees
Closes when: In a real client: one "Open to Nodera" button in the LAN slot, a live public-world count on the world list, three working multiplayer tabs, and a piece map that fills green as pieces arrive
OPEN
The world-identity, authorship, and permission model is landed and enforced, but several halves ride the live mesh: the single-player world-list per-row player count needs the repository's first mixin in a GUI environment; live chunk and region validation over the worker mesh needs the committee stack running against real regions; remote-joiner operator grants; and password-change re-encryption must propagate over the network. Ban enforcement at join and on gossip ingest is done
Closes when: In a real client: a shared world shows a live player count on its row; a committee re-executes and commits a region delta over the worker mesh; a banned peer is refused at join; a re-keyed world's new ciphertext replaces the old across seeders
OPEN
The entity lane's live half. Activation self-bootstraps and has been observed on a real server (entity lane live across regions with the mesh formed; 239 validated/ghost entities across 12 delegated regions with versions advancing; reopen resuming from the store head; a clean-slate pickup delivered exactly once). The scripted pickup and mob drives now pass in CI. The mob drive's seatless half is done: L-60 retired 2026-07-26 with `e2e-mobs.sh` G2 green on a dispatched run — though what G2b asserts changed on 2026-08-06 (#236): refusing a region for an entity the engine does not model was retired as a defect on 2026-07-29, so the stage now asserts that such a species is left to vanilla and the region keeps validating. The rewritten stage has not been run live yet. The pearl drive's ghost half still SKIPs, and the reason is now precise rather than inherited. Refusal and capture are not the same problem. A seatless observer must never hold canonical state without a replica, which is exactly what capturing a ghost is; `ObserverLaneRuntime` therefore no-ops every capture entry point on purpose. (It no longer refuses anything either — see #236 — so it is a *nameable* no-op: the bridge's `LANE:` line prints its class, which is how a live run tells "no seats fell here" from "the lane never activated".) Meanwhile entity capture can only happen where entities live (the server) while seats live on the players' nodes, so on a dedicated server nothing is positioned to capture the pearl at all. What this needs is a protocol addition, not a wiring change: the observer forwarding captured ghost STATE to the region's owner as an external mutation for that owner to certify — the `ExternalDelta`/interference path, which today requires a local replica. That is #45 territory and it is the honest next step. Live-confirmed 2026-07-26 (e2e-live run 30214550400, `pearl` suite): the drive passes E0, E2 and E3 and skips E1, and the server log says why in its own words — `no regions fall to this node in the new plan (2 member node(s), 1 resident peer(s))`, followed by `LANE: Region[...] observed (first entity: polar_bear, capture=true, runtime=ObserverLaneRuntime)`. The seatless runtime installs correctly and declines the capture on purpose. So this row is not waiting on a flake, a harness gap or more wiring: it is the architecture behaving as designed, and only the protocol addition changes it. Also remaining: the certified genesis replacing the interim manifest end to end
Closes when: The scripted gameplay drives pass in CI: validated pickup credited exactly once, mob capture and revocation behaving, and the pearl ghost/materialize/teleport drive green
RETIRING
Block capture only ran on the node whose lane held the region. The live capture lane exists — `BlockCaptureBridge` turns a real place or break into a signed action on the same submit path as entity actions, with `VanillaPalette` + `PaletteMapper` deciding what is consensus state — but its first gate was `delegated(region)`, and under field-of-view ownership a dedicated server holds no seat while being the only process that sees the edits. Fixed out of game: the node that computes the ownership plan now keeps it (`ObserverOwnership`), and a capture in a region this node does not hold is signed and forwarded to the planned primary through `WorkerValidationService.forwardTo` — which, unlike `forwardToPrimary`, starts from the plan instead of a local replica. The observer is a courier, not an authority: the receiving primary re-verifies the actor signature, the admission rule and the batch before proposing anything, so a wrong index entry costs a dropped forward rather than a wrong world. `ObserverOwnershipTest` (5) pins the index, including that a re-publish REPLACES rather than merges — a player walking away must take their regions out of the plan, or the observer forwards into a void. Remaining: the live run, and the actor-key question it will surface (the primary must admit the observer's signature for that actor, which is #45 territory)
Closes when: On a dedicated server with all regions owned by player nodes, a player's place and break reach the owning node's committee and commit: `/nodera debug capture` reports captured edits rather than `REGION_NOT_DELEGATED`, and the resulting delta appears in the region's chain
RETIRING
Peer bring-up runs on the client render thread. `onServerSessionInfo` enqueues onto main and then does a companion exchange, a relay reservation across every endpoint at 5 s/10 s, a second registration pass and the bootstrap dial — about ninety seconds of frozen client with three unreachable relays
Closes when: With every configured relay pointed at a black hole, the client never blocks for more than one frame
OPEN
Host activation runs on the server main thread: genesis, identity, transport start, mesh, worker notify, plus a `Files.walk` of the whole save. On an integrated server this is the singleplayer loading screen
Closes when: World load time is unaffected by an unreachable relay set
OPEN
MC-JOIN-4retiringminecraft10 The endless "Migrating world…" screen. Reproduced from a live run: a password-gated world refuses a joiner during configuration, which produces the same `DisconnectedScreen` a host crash produces. `NoderaContinuity.onScreenOpening` runs on `ScreenEvent.Opening` — *before* `JoinPasswordScreen.onScreenInit` runs on `Init.Post` — so it replaced the screen with the recovery flow and the password prompt was never attached. The player then sat on a screen with no button, no Esc and one unchanging line for the whole 120 s fetch, and got "no seeder online?" while the seeder was online seeding 40 pieces. Four separate faults: the misdiagnosis, the unescapable screen, a `status` field written by four call sites and read by none, and a failure message that asserted a cause it had not checked
Closes when: A refused join reaches the password prompt; every waiting screen can be left; the step being performed is on screen
RETIRING — `ClientJoinPasswordsGateMarkerTest` (3) green;
The rest of this row
the live re-run is the exit
Capture is off by default (`entity.laneAutoActivate=false`), and switching it on is not enough: `mobCaptureSpecies` defaults to zombies alone and any other entity entering a region triggers a permanent revocation of it
Closes when: A default install captures block edits, and a region survives a passive mob
OPEN
Client and server derive different committee membership: the server plans with resident worker seats, the client plans without them because `NoderaLanePlanPayload` carries no resident list. Primaries agree, validator sets do not
Closes when: A client-primary's proposal reaches the seated worker peers and their votes are counted
OPEN
MC-JOIN-7retiringminecraft10 A peer could not enter the world it was helping to host. MC-JOIN-1's rule — no local `mc_route` means nobody can enter — was applied to *every* row from the worker, but `connected_worlds` lists every world in the local registry, including the ones this node only stores for somebody else. A supporting peer therefore stamped `DEAD` on somebody else's live world, and `snapshot()` merged own-rows-first with `putIfAbsent`, so that row displaced the tracker's `HEALTHY · 1 player` and `joinable()` greyed out Join. Observed live: "Hello by Dev · players unknown · 0% reliable · Offline" on the peer, "Joinable now · 1 in this world" on the host, same world, same minute. Fixed by splitting the question: identity (name, owner) is local, liveness is the network's answer unless this node actually hosts the world (`seeding:false`)
Closes when: `MultiplayerWorldFeedTest` — `aSupportedWorldTakesItsLivenessFromTheNetworkNotFromThisNode` and its negative control `aHostedWorldWithItsGameClosedStaysUnjoinableWhateverTheTrackerSays` — green and a live two-client session where the non-hosting peer joins from the Worlds tab
RETIRING — headless proof green, live re-run outstanding
"Multiplayer" and "Nodera Network" open the same screen from two registration sites, with different Back behaviour, a Mojang safety gate on one route only, and an injected button that does not inherit vanilla's `active`
Closes when: The title screen offers Nodera exactly once
OPEN
The status footer is drawn after every widget at the absolute `height - 66`, into a body whose bottom is `height - 64` — over vanilla's list separator on one tab and across both panels on the other
Closes when: No text is drawn over another widget on any screen at any scale
OPEN
Injected widgets use absolute bounds beside vanilla layouts that reposition themselves: the create-world button overlaps the tab bar below width 688, the select-world summary lands on vanilla's title, and the pause-menu fallback lands in the disconnect band exactly when hosting
Closes when: Every injected widget is placed inside its host screen's layout
OPEN
Status cells, badges, piece-map labels and every HUD surface are untranslated `Component.literal` English; two lang pairs are dead duplicates and two panel titles are leftovers from a retired three-tab design. Mostly fixed 2026-07-28, and the hard half was architectural rather than a `literal` sweep: the `diagnostics` view models are Minecraft-free precisely so they can be gate-tested, which is why they had become the place English was assembled — a cell reading `"3 region(s) awaiting quorum"` is a sentence no lang file can reach. `Cell` is now `(key, args, semantic, bold)` with a required key, so a view model *cannot* emit text; `Panel` carries a `titleKey`; `ComponentRenderer.text(Cell)` is the single resolution point, used by the chat tables, `PanelWidget`, `PieceMapWidget`, `NoderaWorldList` and `SelectWorldScreenAddon`. The HUD followed: tab list, the four boss bars and the zone action-bar alerts are `Component.translatable` with 19 new keys. The dead `nodera.share.button` pair and the superseded `nodera.worldlist.shared_summary` are deleted, and the two leftover tab-named panel titles are re-keyed `nodera.multiplayer.panel.*`. Guarded by source scanners that fail on the *next* hardcoded string, not just today's. The command replies followed 2026-07-28: `/nodera` and `/noderac` were the worst case, not the easiest — a reply was a `StringBuilder` handed to `Component.literal`, text no lang file can reach *and* no grep for a literal can find. `CommandTree.reply/announce/fail` and `Panel`/`Row`/`Cell` (the same #113 machinery) are now the only ways out, with 79 keys registered in `CommandLang` with the argument count each call site supplies; `SelfTest`'s `SELFTEST complete:` log line deliberately stays untranslated because `scripts/e2e-commands.sh` greps it, while what the player reads is a key. `CommandsHaveNoHardcodedEnglishTest` fails on prose in a `Component.literal` and on a `Component.literal` given a variable at all, so a reply has nowhere to put a string it built; `CommandLangCoverageTest` checks every key against `en_us.json` for existence *and* placeholder/argument arity. Both verified by planting each regression and confirming the named failure. Remaining: no live client has read the refactored screens — the row's exit test needs a `runClient` pass, which no gate run can perform. One measured string still reaches chat verbatim: `/nodera debug relay`'s metric block, which `EntityLaneRelayMetrics.describe()` formats upstream of the command
Closes when: Every string on screen comes from the lang file
OPEN
R2 — nothing is negotiated before admission. `ClientHello`/`ServerHello`/`ChallengeResponse`/`WorkerActivation` (tags 1–4) exist only in the codec and its tests; no runtime handler constructs or consumes them. Production authenticates the carrier, sends `PeerJoin`, and admits — with no comparison of protocol version, rules version, registry fingerprint, or feature set, and `PeerJoin.clientVersion` is documented as explicitly *not* a compatibility input. Incompatibility therefore surfaces as a dropped frame, a liveness timeout, or an exception thrown from inside `FlatWorldRegionEngine` mid-execution, rather than as an answer at the handshake
Closes when: A peer whose rules version differs is admitted as OBSERVER with a coded reason at the handshake, and never reaches an engine throw — `NegotiationTest.aRulesVersionMismatchYieldsObserver` (+ registry fingerprint, forged signature, claimed identity, wrong network, foreign epoch) and `CrossVersionIT.aPreviousReleaseStaysOnTheNetwork`. `Hello`/`HelloAck` replace tags 1–4, and the body `nodeId` is checked against the transport-authenticated peer before any answer is issued
RETIRING — and until 2026-07-29 it was RETIRING over a class with no production caller;
The rest of this row
see the audit correction above. The handshake now runs on every announce (`PeerRuntime.announceSelf`/`onHello`/`onHelloAck`, `HandshakeRunsInProductionTest`), and what is left is the live mixed-release run
R3 — tolerant readers, unconditional writers. Where several body versions exist the reader accepts a range and the writer ignores it: `SessionKeepAlive` accepts v1–v2 and always emits v2, `RegionProposal` emits v3, `ExternalDelta` emits v2. Compatibility runs one way only — a current peer accepts an old peer's keep-alive and then sends one the old peer cannot parse, so the old peer eventually declares it dead. There is no negotiated downgrade and no per-peer emission profile to hold one
Closes when: A compatibility matrix test — peer(features=A) × peer(features=B) — asserting the emitted set is the intersection — `NegotiationTest.theSelectedFeatureSetIsTheIntersection` + `emissionHonoursTheNegotiatedProfile`. `PeerSession` is consulted at emit time, so a feature the peer did not accept cannot reach it. Consensus-shaped features gate a committee seat rather than bytes, because a proposal's signature covers its body version and cannot be demoted by a sender that did not produce it
RETIRING — the negotiated profile is now held per peer in production (`PeerRuntime.sessionOf`) and the consensus-feature half of the gate bites: `NoderaHost` refuses a resident committee seat to a peer that negotiated OBSERVER.
The rest of this row
The emission half is wired at the same time: `PeerRuntime.sendTo` — the single choke point every membership send passes through — encodes via `PeerSession.encodeFor`, so a `SessionKeepAlive` carrying region progress is demoted to the v1 body for a peer that did not accept that feature, before it is encoded rather than after it fails to parse. A peer with no negotiated session still encodes exactly as before, so nothing regresses on a lost `Hello`. What remains is the live mixed-release run
R5 — ordinal enums at the wire boundary. No boundary enum carries assigned wire codes; all encode `ordinal()`. Some are documented frozen (`PeerRole`, `WorldRole`, `WorldHealth`, `RegionReplicaRole`), several are not (`ContainerAction.Mode`, `EntityKind`, `EntityTransferRecord.Stage`, `Compression`, `DurableActionJournal.Stage`), and one is external: Minecraft's `Pose.ordinal()` is written as a u32 into canonical ghost state, so an upstream refactor silently changes Nodera's hashes. An unknown value also fails the whole message rather than the field — and where it does not, it is lossy: `RegionRefusal` collapses an unrecognised reason to `UNKNOWN`, which re-encodes as `UNKNOWN`'s own ordinal, the one canonicality exception `CanonicalMutationFuzzTest` allows
Closes when: No `ordinal()` on any encode path, enforced by an ArchUnit rule, with an unknown infrastructure code preserved for re-encode — `WireEnumRulesTest` (9). The ArchUnit rule was falsified before being trusted: the obvious spelling `callMethod(Enum.class, "ordinal")` is vacuous, because the call site's bytecode owner is the concrete enum. Minecraft's `Pose` now goes through Nodera's own `PoseCodes` table, with the ghost payload version bumped to 2
RETIRING
A seeder's dial route was discarded and then asked for again. `resolveSeeders` kept only the node id from each `PeerEntry` in the tracker's seeder response, dropping the `route` the entry carries, and relied on a second routes query (tag 49) to supply it. A tracker that does not answer that query leaves every seeder known and unroutable, and the fetch fails with `no routable seeder` while the host is announced on the same tracker. Fixed: the entry's route is kept, both sources share one filter that rejects `mc/` claims, and the lookup logs seeder and routable counts
Closes when: A live join whose worker log shows `Seeder lookup … N seeder(s), N routable` and a completed fetch
OPEN — headless proof now exists: the read half of the tracker is behind the `TrackerLookup` seam, so `SeederRouteSurvivesTheTrackerAnswerTest` (3) can hand in a tracker that answers the seeder query and *not* the routes query — the exact live shape. The route-is-kept case was verified failing without the fix. The row stays OPEN because the exit test is a live join and that has not been re-run
A joining peer could not send to its own bootstrap. The bootstrap address carries a route and no node id — a joiner dials `host:port` before it can know who is listening — and `RendezvousPeerTransport.dispatch` opened with `Objects.requireNonNull(to.nodeId())`, three lines above the code that already honours a caller-supplied route. The NPE is not a `TransportException`, so `PeerRuntime.sendTo`, whose whole contract is that a failed send is not the caller's problem, did not catch it: it escaped `onHeartbeatTick` at its first statement and killed the state-thread worker, sixteen times in one live run. The joiner's membership stayed `{self}`, so an archive download had only whatever seeders the tracker had named and no mesh behind it. Fixed: an unnamed route is dialed directly, a failure there is a `TransportException`, `sendTo` absorbs unchecked failures, and the heartbeat's keep-alive is no longer reachable-past by an earlier step
Closes when: `BootstrapAddressHasNoNodeIdTest` (3) + `HeartbeatSurvivesABadBootstrapTest`, both verified failing without the fix; and a live join whose log carries no `rendezvous transport requires a nodeId`
RETIRING — headless proof green, live re-run outstanding
The P2P lane carries membership and certified state, and committee validation runs over it out of game — but the live mesh has still not been observed carrying validated state. Both product gaps this row had narrowed itself down to are fixed as of 2026-07-29. (1) *The joiner's worker never joined the world its player was in.* `companion.mesh(route, seed)` — the verb that promotes a private daemon into a MEMBER — was called on the host startup path only; the joiner path called `companion.mesh("", null)`, which detaches. So the resident population was capped at one however many workers were running, which is exactly why every run had a single inspectable holder per region and no second root to compare. Fixed: `NoderaPeerService.bindCompanionToSession` is issued from the plan broadcast — the first message a joiner receives that carries the world seed, which the worker's validation lane must be bound to before it can hold a seat — and the detach mirrors it on `stopClient`. The trust question the diagnosis left open is answered by the trust model already in place: membership is not authority, a seat only entitles a node to re-execute and vote, every vote is verified against the key in the plan, and the worker is handed the same bootstrap its own player was handed. (2) *A second, quieter divergence found while fixing the first:* the host planned with `residents.keySet()` and the client planned with the four-argument overload, i.e. with no residents at all, because the resident pool was never in `NoderaLanePlanPayload`. The plan is a shared computation and the two sides were not given the same inputs, so a client primaried a region with a committee of players only and then dropped the votes of the residents the host had seated as coming from outside the committee. Fixed: the pool rides the payload and both sides filter it by the same four rules. Evidence: `ResidentPlanAgreementTest` (5, the divergence verified failing without the fix), `ClientValidationLaneResidentPoolTest` (7), `CompanionSessionBindingIsCalledTest` (5, a call-site guard because the fault was an *absence*) `e2e-mesh-soak.sh` now drives the attempt (32 rounds of edits, mobs and movement over 180 s with two real clients) and reports what is actually there: the players' client lanes re-execute and vote (`active on 11 region(s)`), while all three headless workers report `votes_cast=0, votes_received=0, committee_commits=0`. The seats live on the clients, whose roots the control socket cannot see, so there are no two inspectable peers to compare — the same node question as L-60. The mechanism stays proven headlessly (`WorkerQuorumValidationIT`, `EventSyncOverTransportIT` — the 2026-07-29 audit found `EventSyncService` had no production caller, so that second citation proved a mechanism no peer ran; wired 2026-07-30, so it is now a citation about the product: `LiveEntityLaneSession` serves `EventSyncQuery` on its application-lane arm and asks its committee peers on open for what each region gained while this node was away, guarded by `EventSyncIsWiredTest` (4)). Diagnosed 2026-07-26, and it is the test topology rather than the code. A seat-count diagnostic added to the branch a live host actually takes (the field-of-view case where the server owns nothing) reported `resident peer(s) holding 0 committee seat(s)` on its first run. That discriminates the two candidate bugs: the seats are never *sent*, not sent-and-ignored. Root cause found and fixed 2026-07-26. `RendezvousPeerTransport.availablePaths` decided whether DIRECT was even a candidate by asking only whether the peer had advertised a host candidate through rendezvous, so a send addressed to a perfectly dialable `host:port` counted as relay-only, went to a circuit and failed when none existed. `directAddressFor` had always honoured `to.route()`; the selector was simply never offered DIRECT to choose. Since `NoderaHost` seats an always-on peer by sending `RegionAssigned` to the route in its membership entry, every resident seat in every live run was planned and then dropped — the live log named it as `127.0.0.1:25620 → relay send failed`, on loopback. Fixed, with `CallerRouteIsDirectTest` pinning both directions. Live result: `worker-held region replicas: 0 → 64`. What remains is now a different and narrower question: all 64 are on ONE worker (`peer1`; `peer2` and `spare1` report zero), because the host sees only `1 resident peer(s)` in its session view. With a single seatable resident each region has the player primary plus one validator, so exactly one *inspectable* holder exists and there are still no two independently-computed roots to compare. And that question is now answered too: the other workers never join the session at all. `peer2` and `spare1` log five lines each and elect only *themselves* gateway at epoch 0, while `peer1` joins and sees the mesh. The cause is that `companion.mesh(route, seed)` — the verb that promotes a private daemon into a MEMBER of the world's session — is called on the host startup path only (`NoderaPeerService`, "Nodera host peer online"); the joiner path calls `companion.mesh("", null)`, which unbinds. So a joining player's always-on worker is never part of the world it is playing in. That is a product gap rather than a test artifact: the premise of the always-on lane is that *each* player's worker keeps the world alive, and today only the host's does, which caps the resident population at one however many peers are running. Fixing it means binding the joiner's companion to the session it just joined, and deciding what authorises a worker to join a world its player was invited to — a trust question, not a wiring one. A first explanation — that the suites run too many players for any leftover seat to exist — was tested and disproved: `ResidentSeatingTopologyTest` shows the planner seating residents even in a three-player world, because view discs overlap only partially and many regions are seen by a single player. The population is not the cause, and `NoderaHost` does pass `residents.keySet()` into `EntityLaneBootstrap.plan`. So the fault is further down the live path: what `residents` actually contains by the time `assignResidentSeats` reads it, and whether those ids are the same ones the host advertised. Exit test unchanged: two independently-computed, inspectable roots agreeing over a live mesh
Closes when: A live `nodera-test run mesh-soak` where two headless workers (not the clients) both report non-zero `committee_commits` and their reported roots agree
OPEN — and the 2026-07-30 live run establishes that this exit test cannot pass in this build at all, for a reason no register stated.
The rest of this row
`ValidationLane.DETERMINISTIC_VALIDATION` is a compile-time `false`: deterministic re-execution and committee co-signing are deliberately out of scope for the initial release, and every share says so — *"'world' runs WITHOUT deterministic region validation — the deterministic validation lane is switched off for this release"*. So there are no committees, no votes and no `committee_commits` to observe, and the row's long "what remains is the run itself" history was describing a run against a switched-off feature. Two further facts from the same run, both previously mis-recorded: (a) this environment does have a working X display (`DISPLAY=:0`) and a NeoForge install, so "it cannot be performed in an environment with no X display" was inherited from an earlier session and is not a property of the machine; (b) the run could not even reach its first stage, because `LiveStack` launched the tracker and the rendezvous with `NODERA_TRACKER_BIND`/`NODERA_RENDEZVOUS_BIND` — both services take `*_BIND_ADDR` and refuse to start on an unrecognised variable — and nothing checked they were alive, so every live scenario since the harness conversion (#120, 2026-07-29) ran with no discovery plane whatsoever. That is fixed, and the first run afterwards reached `Seeder lookup … 3 seeder(s), 3 routable`. What this row now needs, in order: the release switch flipped, then the run. The run was then performed with the switch flipped locally (reverted immediately; the shipped default is still `false`), and it is the most informative result this row has ever had. `nodera-test run mesh-soak` passed all five stages: 33 rounds of load, the walking player's lane `active on 11 region(s) — 2 member node(s) + 3 resident peer(s) in the plan`, and `worker-held region replicas: 152` — against `0` before the DIRECT-path fix and `64` on one worker after it. S3 compared what it exists to compare: 67 regions reported, 67 region/version pairs held by more than one peer, 0 pairs where the peers disagreed and 0 not comparable. So the "two independently-computed, inspectable roots agreeing over a live mesh" half of this exit is met, on 67 regions rather than two. The other half is not, and it is now a much narrower question than the one this row has carried: all three workers report `votes_cast=0, votes_received=0, committee_commits=0` while holding 42 committee seats and 152 replicas. Workers hold seats and hold state and agree about it; they do not vote. That is the whole of what is left — plus the release switch, without which none of it runs in a shipped build. Where to start on that counter, from the same run's artefacts: the worker's `NODERA-STATE` reports `validation.active_regions = 67` with roots whose versions advance (one region reached v43), so its lane is active and its state is moving — while its log contains no region-assignment, activation or proposal line at all (153 gateway lines, 31 seeder lookups, nothing about a seat). State reaches the worker without the vote path being exercised, which points at committed state arriving as an external delta rather than at a proposal it re-executes and answers. The next run wants instrumentation on the primary's send: whether `RegionProposal` is addressed to the seated residents at all, before asking why they do not answer. The row stays OPEN: retiring it on a flag this build does not ship, and on half of its own exit clause, would be the exact mistake this register exists to prevent. The second clause of this exit ("or the joiner-companion binding gap is fixed and the same run shows ≥ 2 resident seats") has been removed rather than claimed: the binding gap is fixed, but "the same run" was always the load-bearing half of that clause and retiring a row on the half that is not a run would be the exact mistake this register exists to prevent
No asynchronous client chunk pipeline: a region renders only after its whole snapshot arrives. Correction 2026-07-29: the "edit half is done" claim was false in production and this row moves back to OPEN. `WorldMutationApplier` does consult a `ChunkEditability` seam and `ChunkLockEditability` does adapt the live `ChunkLockMap` to it — but both production constructions (`WorkerValidationService.java:365` and `:2093`) use the one-argument `new WorldMutationApplier(world)`, which installs `ChunkEditability.ALL_EDITABLE`. Every chunk is editable in the shipped product; the adapter is referenced by tests only (`:peer:structureReport` §2.2). The seam, the adapter and its tests are real work and the remaining step is small but not local: the download lane's live `ChunkLockMap` (held by `PieceDownloader`, per world archive) has to reach the validation lane (per region). Correction 2026-07-30: "the edit half's wiring" understated it — there is no lane to wire it to. Two facts found while acting on the wire-or-delete verdict. (1) `ChunkLockMap` has no producer: the single production `download` call (`WorldArchiveService:1255`) passes `null` for the lock map and nothing anywhere calls `track`, so the map is empty and `isChunkEditable` answers false for every region — installing the adapter as it stands would lock the whole world against editing rather than lock an un-arrived section. (2) There is no region-piece fetch at all: regions are seeded and their manifests travel on `WorldManifestAnswer`, but nothing downloads a region's pieces and applies them, so no production path exists in which region content arrives piecewise into a world a player is editing. Building that lane also needs the chunk→piece index, which lives only in `RegionSnapshotSplitter.Layout` — derived from the snapshot the fetcher does not have — and does not travel with the manifest; a coarser "the region is locked until every piece has arrived" would need no wire change and is the cheaper first step. Update 2026-08-04: both blockers named above are now built. The region-piece fetch lane exists — `WorldArchiveService.fetchRegion` resolves a region's manifest by chunk-index root, downloads the subset of pieces the asking peer does not already hold, verifies the assembled blob against the certified region root and decodes it; `NODERA-FETCH-REGION` carries it across the control plane, and `LiveSnapshotApplier` + `RegionApplyQueue` write the result into a live `ServerLevel` a few columns per tick, inside the applier scope. The chunk→piece index now travels: region snapshots are cut one piece per chunk column and `PieceManifest` v2 carries `pieceOfChunk`, so a fetcher maps changed columns to pieces without holding the snapshot. Remaining, therefore: the edit guard installed on the new lane (`ChunkLockEditability` still has no production construction), and the render-on-arrival half in a GUI environment
Closes when: Pieces render on arrival; an un-arrived section is locked against edit through a production call path, not only in tests — a test that fails when the applier is constructed without the lock map; the manifest hash validates before render; reassembly from seeders each holding < 40%
OPEN
The Minecraft mod still composes its rendezvous transport from a static `NoderaConfig` list read at world open, so the worker's discovered-and-scored selection reaches the always-on node but not the in-game transport. A relay that drains mid-session therefore migrates the worker immediately and the mod only at the next world open. *(Renumbered 2026-07-28 from a duplicate L-84; see the hygiene note above.)* The code half is done and unit-tested: `WorkerStateParser.rendezvousRoutes` reads the worker's live selection out of `NODERA-STATE`, `composeHostTransport`/`composeClientTransport` seed from it (config remains the fallback, never a second-class one), the `RendezvousPeerTransport` handles are held so `setEndpoints` can replace the list mid-session, and the announce cadence pushes changes in. 8 tests in `WorkerStateParserRendezvousTest`, `./gradlew check` green
Closes when: A live suite where a relay drains mid-session and the in-game transport re-registers with the replacement without the world being reopened
RETIRING — it stays RETIRING because the exit test is a live suite and that suite does not exist yet: nothing here proves the loop closes against a relay that actually drains
Reliability is a single proposal-outcome EMA. `ReliabilityLedger` holds one scalar per node and moves it by `score ← (1-α)·score + α·outcome` (α = 0.02) on whether that node's vote matched the committed root, plus a one-shot cap below the assignment floor after a lag handoff. Nothing else is blended in: a peer that is fast, well-connected, always online and seeding twenty worlds is indistinguishable from one that has answered two proposals, and a peer whose only fault is a bad link is scored as if it had computed the wrong world. This is history, not a regression. The row retired on 2026-07-23 on `ReliabilityScorer`, which blended five factors in basis-point integer arithmetic; all three reliability classes and their test were deleted on 2026-08-06 (commit `24e6f0e`, issue #210) because no production entry point could reach them, and the check on `24e6f0e^` establishes they never had a production caller at any point — the multi-factor score was a complete, correct, unreachable mechanism, so nothing observable changed when it went. The row returns because a retirement must rest on a runnable check and this one rested on a suite over a class the product never loaded. Evidence preserved under "Withdrawn retirement" in `LIMITATIONS.fixed.md`; `git show 24e6f0e^:library/java/engine/src/main/java/dev/nodera/coordinator/ReliabilityScorer.java` recovers the implementation
Closes when: No exit test can be run on this tree, and what has to exist first is a production reader, not a scorer. Restoring `ReliabilityScorer` and `ReliabilityScorerTest` verbatim does not retire this row: that is the state it was retired in before, and it proved nothing about the product. Two things have to be true first. (1) The signals have to be collected in production. Correctness is the only one the running system observes — `WorkerValidationService` folds committed-round agreement into the ledger, and `CommitteeFailover` writes the lag penalty. Connectivity, uptime, availability and worlds-seeded are measured nowhere: `NodeCapabilities.reliability` is a wire field that no production code ever sets (`withReliability` has zero non-test callers), so the value `GatewayElection` already weights is the constant every peer advertises. (2) A production decision has to consult the blended value. Today no production code reads a reliability score to decide anything at all — `ReliabilityLedger.eligibleForAssignment` and `slash` have no non-test callers, and the only production read of `score` formats a log line. With both in place the exit test is: a peer whose votes always match the committed root but whose link is bad is ranked below an equally correct peer with a good link by a decision the product actually takes — a gateway election or a region assignment — with the test verified failing when the extra factors are removed from the blend, and the score proven bit-identical across two independently-computed peers, because placement and election must agree everywhere
OPEN
Nothing measured NoderaMC in the wild. The emitter now exists — `dev.nodera.telemetry` (consent gate, bucketing, bounded spool, sender), proven by `TelemetryEmitterTest` (21), the cross-language `TelemetryRegistryMirrorTest`, and `scripts/e2e-telemetry.sh` end to end against the real binaries. What is still missing is the thing the row is actually about: evidence from the wild, which needs a deployment with a population that has opted in
Closes when: The first dashboard answering a `plans/Plan.6.md` §1.1 question from real reports, cited in `PROGRESS.md`
RETIRING
A world archive being downloaded could be evicted mid-transfer. The retention policy (`supersedeOlderVersions`, L-55) runs when this node *learns* a newer version exists. On a live world the host streams a new archive version every couple of minutes, so a joiner fetching a large archive had the manifest root its `PieceDownloader` was writing into unpinned and unpublished underneath it — the download could never complete, and the player sat on "Migrating world…" until the fetch deadline. Observed live: 913 KiB downloaded, 0 pieces retained. Fixed by making an in-flight root eviction-proof for the duration of the fetch
Closes when: `FetchSurvivesSupersessionTest` green and a live join to a world whose host is playing
RETIRING — headless proof green, live re-run outstanding
A world this node claimed but held nothing of was never repaired. The replication sweep skipped any world where `holdsCompletely(...) || hosts(...)` — but `hosts()` reads the registry claim, not the content store, and `restoreFromRegistry` reloads every row as hosted at boot whether or not the bytes survived. So a node announcing a world it could not serve was disqualified from fetching it, permanently and self-perpetuatingly. Observed live: "Yours — hosted here · 0.0% · 0 of 73 pieces" beside "3 peers holding it besides this node". Fixed: the skip is about content held, and a claim with nothing behind it is repaired ahead of placement and bounds
Closes when: `ReplicationRepairsEmptyClaimsTest` green (verified failing without the fix) and a live node at 0% reaching 100% within one sweep
RETIRING
A peer that completed one version of a world never took another. "Complete" was read off whichever manifest this node happened to hold, and both paths that could have moved it forward stopped there: the replication sweep skipped any world it held completely, and `fetchArchive` served a held complete copy without asking the network at all. A peer that fetched a world minutes after it was shared then seeded that fossil for the rest of the session. Observed live, two machines in one world side by side: `100.0% · 7 of 7 pieces · v2 · 1.7 MiB` against the host's `100.0% · 208 of 208 pieces · v6 · 51.8 MiB`. A second defect hid inside it — `PieceDownloader` completed its blob future *before* the last verified piece was written to the content store, so a caller handed a finished world could still read "no complete copy here". Fixed: the version question is asked every time and answered on evidence (a bounded probe for what the swarm actually seeds, the held copy kept whenever the probe finds nothing newer, nothing at all, or a version that stalls), and pieces are persisted before completion
Closes when: `PeerCatchesUpWithTheHostTest` green (all three cases: catch-up, no-op when current, held copy survives an unreachable newer version) and a live two-machine session where the joiner's version tracks the host's
RETIRING — headless proof green, live re-run outstanding
One unanswerable world parked the whole replication lane. A seeder that holds nothing of a world still answers the manifest query — with an empty list — and `onManifestAnswer` completed the pending future only for a *useful* answer. So a swarm in which every peer honestly said "nothing here" (the ordinary state before a host first archives) was indistinguishable from one that never replied, and the fetch waited out its whole 5-minute budget on the sweep's single thread, blocking every other world behind it. The failure was also invisible: `adopt` logged it at DEBUG. Live evidence: a peer logged "Supporting world 'Hello' — fetching its archive from the network" and then produced no further line for eleven minutes, followed by a summary of all zeroes. Fixed: an empty answer counts against the asked-seeder count and ends the wait, manifest discovery is bounded separately from the transfer budget, and adopt failures are reported at INFO with a reason and counted in the sweep summary
Closes when: `PeerCatchesUpWithTheHostTest.anEmptyAnswerEndsTheWaitInsteadOfTimingOut` green (fails by taking the full deadline without the fix) and a live sweep that reports a reason for every world it did not adopt
RETIRING — headless proof green, live re-run outstanding
Full custody is a claim nobody checks. A node advertising `custody: FULL` is believed; nothing samples a region and verifies it, so an endpoint that has silently lost half its world still reads as a complete replica to the swarm. The gap is narrowed rather than closed: every piece a node serves is hash-verified by the receiver against the manifest, and since the "no negative on the piece wire" fix a peer asked for pieces it does not hold answers with a `ContentAvailability` naming what it actually has, so a liar fails to answer and is passed over. That is detection at fetch time, by the fetcher — after every replication-factor decision that was made on the strength of the claim. This is history, not a regression. The row retired on 2026-07-28 on `CustodyDigest`, `CustodyAudit` and `CustodyAuditIT`; all four custody classes were deleted on 2026-08-06 (commit `0b02aa5`, issue #210) because no production entry point could reach them, and that deletion was the correct call on a closed, unreachable loop. The row returns here because this register's rule is that a retirement rests on a runnable exit test, and this one no longer exists in any form. The retirement evidence is preserved verbatim under "Withdrawn retirement" in `LIMITATIONS.fixed.md`, and `git show 0b02aa5^:peer/src/main/java/dev/nodera/peer/archival/CustodyDigest.java` recovers the implementation
Closes when: No exit test can be run on this tree, and none can be written until custody is carried on the wire. What has to exist first is task 3 deliverable 3 — the custody digest on the tracker announce and on the membership gossip — because only then does an auditor receive a root it did not compute itself, which is exactly the property whose absence made the deleted audit unreachable. With that in place the exit test is: an endpoint that has lost one region of sixty-four and advertises a digest internally consistent over the regions it still holds is sampled by another peer, survives the samples that miss the loss, is caught the moment the lost region is sampled, is downgraded to `VIEW` rather than evicted, and the world stays available across the downgrade — re-served and re-verified for all sixty-three surviving regions afterwards. Restoring the deleted `CustodyAuditIT` on its own does not retire the row: that suite handed the audit a root it had computed itself, which is why the mechanism never ran in production
OPEN
A delta spanning two Folia regions is refused, not applied. Folia cannot put two region threads in one critical section, so a multi-region delta whose regions do not share an execution thread aborts with a named error and the caller resyncs. Correct, but it makes cross-boundary entity handoff and contraption migration a retry loop instead of a commit
Closes when: `CrossFoliaRegionCommitIT`: a prepare/commit across two Folia region threads, certified with joint transfer certificates and journalled with durable transfer stages, commits atomically — and a failure on one side commits neither
OPEN
Foreign-write certification has never met a real plugin. The interference guard converts foreign writes into certified `ExternalDelta`s and is proven headlessly, but no WorldEdit, no protection plugin, and no logger has ever written into a delegated region on a running endpoint
Closes when: `scripts/e2e-plugins.sh`: the pinned corpus loads, a WorldEdit `//set` in a delegated region is certified rather than suppressed or silently reverted, CoreProtect still logs every change, and the log audit is clean
OPEN
The version pin is unresolved, and it is now measured rather than suspected. The mod pins Minecraft 1.21.1 / NeoForge 21.1.238 / Java 21. Paper has a 1.21.1 build (1.21.1-133) and `e2e-endpoint.sh` is green against it. Folia does not: its published versions go 1.19.4, 1.20.x, 1.21.4, 1.21.5, 1.21.6, 1.21.8, 1.21.11, 26.1.2 — 1.21.1 was skipped, so no Folia build of the mod's own Minecraft version exists to resolve. `e2e-folia.sh` is green against Folia 1.21.4, which is sound for plugin enable and ALIGN-1 (platform properties) and useless for anything a 1.21.1 client must join. Also note the PaperMC v2 API is sunset (HTTP 410); resolution now goes through `fill.papermc.io/v3`. The row is therefore a decision, not a task: either the mod's pin moves to a Minecraft version Folia publishes, or the mixed-client Folia suites are dropped and Folia support is asserted only at the platform level
Closes when: `e2e-endpoint.sh` and `e2e-folia.sh` are both green against Paper and Folia builds of the Minecraft version the mod pins, resolved through the PaperMC v3 (fill) API in CI — which requires the pin and Folia's published versions to intersect
OPEN
Vanilla scheduled ticks cannot be cancelled at the source. The mod's `LevelTicksMixin` makes the engine the only scheduler in a delegated region; Bukkit has no equivalent and a plugin may not add a mixin. So an endpoint's delegated regions run vanilla redstone and reconcile every edge through the interference guard — correct, but it converts redstone activity into external-mutation throughput
Closes when: `e2e-folia.sh` F5: a delegated region under sustained redstone load commits for five minutes with the resync count under threshold, and no region is revoked for interference rate
OPEN
Tenants have no cryptographic join gate. L-52's live-join password runs in the configuration phase over a mod payload a vanilla client cannot answer, so on an endpoint the world password is enforced by a spawn-locked limbo and a `/nodera join <password>` command — the endpoint's gate, not the network's
Closes when: `e2e-endpoint.sh` P7: a wrong password never reaches the world, retries are throttled and then disconnected, the right password admits, and no plaintext appears in any log
OPEN
Validated items keep a vanilla-ticking projection. The mod cancels a validated item's vanilla tick so only the canonical item moves; Bukkit cannot cancel a tick, so the endpoint pins the projection instead (unlimited lifetime, zeroed velocity, held pickup delay) and reconciles rather than suppresses
Closes when: `e2e-endpoint.sh` P6: a validated item on an endpoint neither despawns nor drifts over the hold window, and picking it up credits exactly once
OPEN
Mod-set gating is an assumption, not a confirmed requirement. "NeoForge players may join except where the host requires specific mods" is read as: the endpoint declares required/allowed mods and `registryFingerprint` equality enforces it. The alternative reading — accept whatever the client brings — would need a palette-superset check with real determinism consequences. The current rule also admits or refuses whole clients, with no partial-compatibility path
Closes when: The reading is re-confirmed (or replaced) with the requester, and `e2e-endpoint.sh` P8 proves a fingerprint-mismatched client is refused with a message naming the offending mod
OPEN
The compose stack is single-node: no replication, no backup, no restore procedure. A disk loss loses collected history — which costs insight only, never correctness
Closes when: A documented and exercised backup/restore of the ClickHouse volume in CI
OPEN
Nothing has been collected against a real opted-in population yet, so every claim about what the pipeline *shows* is untested against real data. The receiver and the emitters are proven; the population is not
Closes when: The first dashboard answering a §1.1 question from real reports, cited in `PROGRESS.md`
OPEN
The harness launched services and never checked they were alive, so a service that refused its own configuration produced a stack that looked started (see the audit note above). `awaitListening` closes it for the tracker and the rendezvous. What is not closed: the same class starts a dedicated server and two Minecraft clients, and their readiness is asserted by scenario-specific log-watching rather than by one harness rule — so the *shape* of defect can still recur one process type over.
Closes when: every process `LiveStack` starts is proven answering by the harness before a scenario's first stage runs, with the failure naming the process and its log tail
RETIRING
The converted scenarios have not all been executed live end to end since the conversion. The classes carry the same stages, evidence strings and timeouts as the shell suites they replace, and the tool builds and enumerates them, but a green nightly across the whole matrix is what turns "converted" into "proved".
Closes when: a green `e2e-live` run over the full matrix
OPEN
`NODERA-TEST DRIVE` publishes an action on the worker's event stream, but the mod does not yet consume `test.drive` — so a scenario drives a player through RCON (dedicated/Paper) rather than through its own worker. The role plumbing is what a driven player will use; the consumer is not written.
Closes when: a scenario that moves a player with `NODERA-TEST DRIVE` alone and asserts the move in the game log
OPEN
Five harness capabilities live in scenario support classes rather than in the harness, each marked `// HARNESS-GAP`: amending the *staged world's* `nodera-server.toml`, staging `options.txt` (without it `onboardAccessibility` defaults true and quick play never runs — the original CI "never joined" cause), killing the game JVM that is a child of the Gradle *daemon* rather than of the launcher, auditing errors from a mark rather than from the top of the file, and case-insensitive/last-match reads after a mark. Every scenario that needs them goes through the support class, so nothing is missing — but two places now describe one behaviour.
Closes when: `grep -rn "HARNESS-GAP" library/java/testing/src` returns nothing
OPEN
`scripts/lib/` still exists (`e2e-main.sh`, `e2e-server.sh`, `spark.sh`, the Python helpers). Nothing in the test lane uses it; `scripts/dev.sh --play` does. Until the developer playground is ported, two launchers describe the same topology and can drift.
Closes when: `grep -rl e2e-main.sh scripts/` returns nothing
OPEN
`continuity` cannot be run to completion on a 14 GB developer box, and it fails in a way that reads as a product defect. The scenario runs two Minecraft clients, three workers, a tracker and a rendezvous at once. On 2026-08-04 three consecutive runs on an idle machine put the host's integrated server 53 s / 1066 ticks behind (`Can't keep up!`), which drops the joiner's *vanilla* connection (`Client disconnected with reason: Timed out`) 35 s before the host finishes planning its lane. S2c then fails with `waited 180s for 'client validation lane active'` — a message that names the validation lane and says nothing about the machine, so the run looks like a lane bug. The stages under test (S4/S5) are never reached. Not a flake to retry around: the topology is above what the box sustains, and no timeout increase fixes a dropped TCP connection.
Closes when: `continuity` completes S5 on the reference machine, or the harness fails S2c with "the host server fell N ticks behind" when the game is the cause rather than the lane
OPEN
The self-update lane verifies release integrity, not provenance: the digest it checks comes from the same release as the binary, so whoever can publish to the release can publish a matching digest
Closes when: a test that a validly-digested but wrongly-signed manifest is refused by `update::check`
RETIRING — the mechanism is built and tested.
The rest of this row
`update::check` now fetches `SHA256SUMS.sig` and verifies it against a pinned Ed25519 key before it reads a digest (provenance before integrity: checking the digest first lets a substituted manifest choose the binary before anyone asks whether the manifest is genuine). A missing signature is a refusal, not a fallback, or anyone able to delete one asset turns the check off. `scripts/release.sh --sign` (called by `.github/workflows/release.yml`) signs with OpenSSL and verifies what it just produced. The OpenSSL and `ed25519-dalek` halves were cross-checked against each other by hand. Exit test green: `a_validly_digested_but_wrongly_signed_manifest_is_refused` (in `library/rust/nodera-service`), plus the accept and missing-signature cases. What remains is not code: no signing key exists yet. `DEFAULT_RELEASE_PUBLIC_KEY` is empty, which means the lane still checks integrity only and says so on every check. It retires when a key is generated, its private half stored as the `NODERA_RELEASE_SIGNING_KEY` secret and its public half compiled in — a credential-creating step that belongs to the project owner, not to this branch. Audit 2026-07-28: unchanged; the row is verified still-RETIRING against the current tree. Re-triaged the same day with the question "can the remaining half be implemented here?", and the answer is no, for a reason worth writing down rather than re-deriving: provenance is a claim about who published, so the only thing that can close it is a private key that exists outside this repository. Every variant of doing it in-branch is the same mistake in different clothes — committing a generated private key, or letting an unsigned/absent manifest fall back to a digest check, both hand the property to exactly the attacker the row describes. Nothing in the code is waiting on a change: the verification order, the refusal on a missing signature, and the workflow's sign-and-verify step are all in place and tested. The row closes when the project owner generates the keypair, stores the private half as `NODERA_RELEASE_SIGNING_KEY` and compiles the public half into `DEFAULT_RELEASE_PUBLIC_KEY` — one credential-creating step, not a code change.