docs(runicnpc): the cutover's gate 1 asks Steam when public was set (D397)

The 2026-11-05 weekend never cut over: gate 1 wanted the public depot manifest
dated the wipe day and equal to staging's, and neither holds. The wipe build
went live dated 11-04, and Facepunch promotes a hidden `release` branch, not
staging. D397 records the new gate 1 (steamcmd's branches.public.timeupdated on
the wipe day or later, and Steam's public manifest equal to the one downloaded),
the alternatives, and what the first weekend showed; D332 points at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014HJEmgsXtLR5kLfh7f9y8P
This commit is contained in:
2026-11-06 04:39:34 -06:00
parent c7fbf3291c
commit 2013db016e

View File

@@ -155,7 +155,8 @@ architectural or design decision is implemented.
| **D329** | **The Kits reload the first load asks for is owed until Kits has the example kits.** The reload ran on a one-second timer, and a timer dies with its plugin. In the 9d session RunicNPC was loaded again inside that second on Carbon, the reload was lost, and since `examplesWritten` was already set the examples stayed refused until Kits was reloaded by hand. Now `state.json` holds `kitsReloadOwed`: every load, and Kits coming back, clears it once Kits knows every kit owed, and otherwise asks for the reload again, at most three times, then warns with the command to run. Oxide never showed it, because loading RunicNPC recompiles and reloads Kits there (`// Requires: Kits`). The org lead chose the fix over recording it (2026-10-09). | Record it as a hot-load artifact, with the remedy in INSTALL.md. |
| **D330** | **RunicNPC's stage 9 release is v1.0.0, cut by a `feat!:` commit.** The release engine takes its bump from the non-merge commit subjects since the last tag. Every stage had landed as `feat:`, which would have cut v0.2.0. runicnpc-rust#18 (`feat!:`, with a `BREAKING CHANGE:` footer) changed only the README, and made the cutover cut v1.0.0. The API stays 6 (2026-10-09). | Accept v0.2.0 and change the plan's wording. |
| **D331** | **A kit's extra is found by its item definition when it has no held entity.** Rust's November 2026 build adds the server variable `ItemModEntity.playerOnlyEntityBeltOnly` (on by default): an item Rust marks player-only, the medical syringe among them, has a held entity only while it is in the belt. A kit puts its syringes in the main inventory, so D260's heal, which looked for an item whose held entity is a `MedicalTool`, found none and never healed (runicnpc-rust#20). Every extra (heal, melee, flamethrower, grenade, rocket) now reads the held entity when there is one and the item definition's entity prefab when there is not; holding the item moves it to the belt, where Rust makes its entity. A full belt means no heal, not an error. The org lead chose all five extras over heal alone (2026-11-02). | Fix the heal alone. |
| **D332** | **On a forced-wipe weekend a workflow merges `edge` into `main` on its own, once five gates pass** (9c, the automated cutover): Rust's public build changed on wipe day, to the build staging names; Oxide's public release has caught up; a Carbon boot on public Rust says `missing=- added=-`; the drill's live half passes on public Rust with `edge`'s code; PR checks pass. It runs hourly from Thursday 17:00 UTC to Sunday 23:00 UTC, then leaves it to the org lead. The issues it closes are the `Closes #N` lines of the PRs merged into `edge` since the last release; it merges with a token locked to `runicnpc-rust` (`RUNICNPC_TOKEN`), pinned to the `edge` commit the gates ran on. A failed Carbon or live attempt cools for 3 hours. RunicNPC only. The org lead chose it (2026-11-02): gate 1 is "public changed", since the daily drill tests `main` and so cannot vouch for `edge`; the schedule reaches `main` once by a `ci:` PR. | Approve the cutover by hand each wipe (D226 as it was); gate on "the staging drill passed this build"; give up Friday 23:00 UTC. |
| **D332** | **On a forced-wipe weekend a workflow merges `edge` into `main` on its own, once five gates pass** (9c, the automated cutover): Rust's public build changed on wipe day (how that is read: D397); Oxide's public release has caught up; a Carbon boot on public Rust says `missing=- added=-`; the drill's live half passes on public Rust with `edge`'s code; PR checks pass. It runs hourly from Thursday 17:00 UTC to Sunday 23:00 UTC, then leaves it to the org lead. The issues it closes are the `Closes #N` lines of the PRs merged into `edge` since the last release; it merges with a token locked to `runicnpc-rust` (`RUNICNPC_TOKEN`), pinned to the `edge` commit the gates ran on. A failed Carbon or live attempt cools for 3 hours. RunicNPC only. The org lead chose it (2026-11-02): gate 1 is "public changed", since the daily drill tests `main` and so cannot vouch for `edge`; the schedule reaches `main` once by a `ci:` PR. | Approve the cutover by hand each wipe (D226 as it was); gate on "the staging drill passed this build"; give up Friday 23:00 UTC. |
| **D397** | **The cutover's gate 1 asks Steam when the public branch was set, not the depot manifest's date or staging's manifest.** It reads steamcmd's app info for 258550 (`+app_info_print`, anonymous, kept by the public static half as `appinfo.txt`): `depots.branches.public.timeupdated` must be on the wipe day or later, UTC, and the Linux depot (258552) manifest Steam names for `public` must be the one the static half downloaded. The first gate 1 blocked the whole 2026-11-05 weekend: the wipe build went live that evening with a manifest dated 11-04 (the date is when Facepunch built it), and public never names staging's manifest (Facepunch promotes a hidden `release` branch, "Release, before public", and staging had moved on before the wipe). The later gates, the live drill above all, still prove the plugin on the build that is really live. The org lead chose it (2026-11-06). | A baseline: the public manifest the first hour of the weekend sees, passing once it changes (and is dated at most 3 days before the wipe); drop gate 1 and lean on gates 2 to 4. |
**Borrowing, not copying.** NpcSpawn states no licence at all, so its source grants us nothing and is read only as a
description of *what* can be done in Rust. HumanNPC is MIT on uMod, which is GPL-compatible, but §1.2 rules out its
@@ -1818,11 +1819,20 @@ other four Rust repos keep their cutover by hand (D313); a case for them is a pl
runs outside the first-Thursday weekend. It shares the drill's concurrency group, `staging-drill`, so it never
runs while the daily drill has `rust-staging`. Nothing to merge (`edge` already in `main`) ends it at once.
- **Five gates, cheapest first; it merges only when all pass:**
1. **Rust's public build changed, from staging.** The drill's static half runs twice, `BRANCH=public` and
`BRANCH=staging`: the public depot manifest must be dated the wipe day or later AND be the same manifest
staging names, so the staging build was really promoted to live (the org lead's condition: Facepunch
sometimes holds or changes it at the last minute). If Facepunch puts a newer build on staging before public
is seen, this waits to the deadline and hands back, which is safe.
1. **Steam set Rust's public branch on the wipe day (D397).** The public static half (`BRANCH=public`) also
runs steamcmd (`+login anonymous +app_info_update 1 +app_info_print 258550`; it fetches steamcmd and, on a
bare runner, the 32-bit `lib32gcc-s1` it needs) and keeps the output as `appinfo.txt`. The public branch's
`timeupdated` must be on the wipe day or later, UTC, and the Linux depot manifest Steam names for `public`
must be the one the static half downloaded; when the two reads disagree (a push between them), the hour
blocks and the next tries again. A steamcmd failure is the static half's `steamNote`, and the gate blocks.
Facepunch holding or changing the update at the last minute (the org lead's condition) shows as a public
branch not yet set, or set again; a hotfix is a new manifest, so every gate runs again on it.
*As first built*, gate 1 wanted the public depot manifest dated the wipe day AND equal to staging's
manifest, and blocked every hour of the 2026-11-05 weekend: the wipe build (manifest
`3647035647797862067`) went live that evening dated 11-04, a hotfix (`547616323393184824`) followed on
11-06, and staging named neither (it was `2915291852004903570`, then `4006809933087453123`). Steam's app
info shows why: Facepunch promotes a hidden `release` branch ("Release, before public") to `public`, and
`last-month` keeps the previous build.
2. **Oxide has caught up.** The public static run reports Oxide's public release `0` members behind Rust's public
assemblies (D317), RunicNPC from `edge` compiles against them, and the swap's field list is the same as
public Rust's.