Commit Graph

126 Commits

Author SHA1 Message Date
885485d153 ci(rust): pin core to the sha website#218 merged as
All checks were successful
PR Checks / client-build (pull_request) Successful in 26s
PR Checks / frozen-manifest (pull_request) Successful in 51s
PR Checks / server-tests (pull_request) Successful in 7m14s
#218 (Steam sign-in, MODULE_API 1.17.0) merged into website edge as
9a1654f. The pin pointed at the PR's branch head, 5e398d8, while it was
open. The two trees differ by one core test file, so routes.manifest.json
does not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-21 00:38:15 -05:00
47da3a2db8 Merge pull request 'feat(rust): a Steam account linked on the site is a Rust link (D392, D396)' (#52) from feat/steam-signin-link into edge
Reviewed-on: #52
2026-10-20 15:25:44 +00:00
8172454683 ci(rust): pin core to website feat/steam-signin (MODULE_API 1.17.0)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 37s
PR Checks / client-build (pull_request) Successful in 21s
PR Checks / frozen-manifest (pull_request) Successful in 48s
coreApi is ^1.17.0, which no core on main offers yet, so the frozen-manifest
job is proved against the head of the open website PR. Moves to the sha it
merges as.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-19 20:28:25 -05:00
b89039142e feat(rust): a Steam account linked on the site is a Rust link (D392, D396)
Registers core's MODULE_API 1.17.0 identity hooks (coreApi ^1.17.0):
onIdentityLinking refuses a Steam id that is another website account's Rust
link (D396); onIdentityLinked writes the Rust link with the PRIMARY KEY as the
race guard, reconciles Teams and raises the linked notice, as /link does.
Unlinking Steam leaves the Rust link (D393); the Rust account page says so.

ci/core-ref.json still pins the 1.15.0 core and must move to the website
branch head carrying 1.17.0 before CI can pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-19 14:02:53 -05:00
f93ba86883 Merge pull request 'feat(rust): RaidableBases — feed, raids map layer, Warlord, Spawn a raid base, profiles (PLAN_REDESIGNS §11.4)' (#51) from feat/raidable-bases into edge
Reviewed-on: #51
2026-10-17 06:33:08 +00:00
481837e937 feat(rust): RaidableBases — feed, raids map layer, Warlord, Spawn a raid base, profiles (PLAN_REDESIGNS §11.4)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 37s
PR Checks / client-build (pull_request) Successful in 24s
PR Checks / frozen-manifest (pull_request) Successful in 49s
PLAN_REDESIGNS §11.4 (D202-D208, D386-D391), protocol 14. Spec: docs
rust-link/PROTOCOL.md §20.12.

- raid.started, raid.completed, raid.purchased and raid.locked public,
  raid.ended staff's; raidableBases.js reads each row per viewer (raiders'
  and a buyer's names only for the presence audience, D376/D390; Steam ids,
  position and a run's key only for staff). Feed lines in the World filter.
- The step rust.raidbase.spawn, "Spawn a raid base" (ledgered, budget
  rust.raids): difficulty, profile and base from option sources listing each
  server's own names (D386), a spot as a world call's location with a height
  adjustment or an exact height, and PvP/NPCs/lock as on/off/blank. A base
  the raids helper made is a world resource, so eventWorld's revert and
  reconcile serve it and world.expired marks it expired; one it did not make
  records nothing and the run log says it despawns on its own timer. Refused
  at verify for a server that said it cannot; a dry run with a spot returns
  the spot check as detail.warnings (MODULE_API 1.16.0, D391). rb-not-ready
  retries in ten minutes.
- Warlord: raids_completed, title category raidscompleted, the leaderboard's
  Raid bases column (sort=raidsCompleted).
- The map's raids layer (D114), public by default with its own audience; the
  run's key is dropped before any viewer gets it.
- Server settings' line for RaidableBases; its profiles in Mod configuration
  under a virtual path (D205).
- Swagger fragment, bundle and README.

Tests: server 684/684 (raidableBases.test.js new, every name in it invented);
client 99/99, build and externals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-16 15:18:52 -05:00
49d9e42a23 Merge pull request 'feat(rust): Run console commands and Reward by console command (PLAN_REDESIGNS §16)' (#50) from feat/command-steps into edge
Reviewed-on: #50
2026-10-16 07:47:28 +00:00
5686d9fd17 feat(rust): Run console commands and Reward by console command (PLAN_REDESIGNS §16)
All checks were successful
PR Checks / client-build (pull_request) Successful in 23s
PR Checks / server-tests (pull_request) Successful in 29s
PR Checks / frozen-manifest (pull_request) Successful in 56s
Two event steps in server/eventCommands.js, on one new budget,
`rust.commands` (D382-D385):

- rust.command.run: up to 10 typed commands once on one server, with
  optional teardown commands. irreversible / ledger: the ledger row keeps
  the teardown as it was, and core reverts it when the run ends or is
  cancelled. Priced one unit per command, teardown included.
- rust.command.reward: the kit reward's recipient picker (tally modes and
  linked), by Steam id, so no website account is needed. The bridge fills
  {steamid} and {name}; offline recipients are skipped and named (D383).
  irreversible / none. Priced commands x people, the bound for a mode that
  is not a count.

Both take core's `multiple` string field as a free text box (no
allowlist, D384) and a required "I am sure" toggle. {steamid}/{name} in a
run step are refused. unknown-command, malformed, events-disabled,
interrupted are permanent; the error says how many ran before it.

D385: the run also hands its teardown to the bridge, which keeps it with
the key. A revert with only the key (the step failed, or its answer was
lost, so core recorded nothing) asks every enabled server to run what it
kept; a server the step never reached runs nothing.

sidecarClient.commandsRun -> POST /commands/run; ci/bundle.json lists the
new file; entry.test.js pins the new ids; 21 tests in
test/commandSteps.test.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-16 02:41:10 -05:00
619081ec65 Merge pull request 'feat(rust): ZLevels Remastered on the servers page (PLAN_REDESIGNS §14.3.6)' (#49) from feat/zlevels into edge
Reviewed-on: #49
2026-10-16 06:10:28 +00:00
9c82a0c437 feat(rust): ZLevels Remastered on the servers page (PLAN_REDESIGNS §14.3.6)
All checks were successful
PR Checks / client-build (pull_request) Successful in 25s
PR Checks / server-tests (pull_request) Successful in 31s
PR Checks / frozen-manifest (pull_request) Successful in 51s
Stage 7 of the §14.3 integration plan (D374): Server settings' plugin
line says whether ZLevels is loaded and that an event may lend its XP
boosts as permissions. The integrations schema gains zLevels; the
swagger fragment is regenerated. No leaderboard change, nothing stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 18:27:57 -05:00
8be7e5607e Merge pull request 'feat(rust): Guarded Crate — feed, map, Warden, Place a guarded crate (PLAN_REDESIGNS §14.3.5)' (#48) from feat/guarded-crate into edge
Reviewed-on: #48
2026-10-15 23:19:14 +00:00
da1d74bdff feat(rust): Guarded Crate — feed, map, Warden, Place a guarded crate (PLAN_REDESIGNS §14.3.5)
All checks were successful
PR Checks / client-build (pull_request) Successful in 30s
PR Checks / server-tests (pull_request) Successful in 34s
PR Checks / frozen-manifest (pull_request) Successful in -1m34s
Stage 6 of the §14.3 integration plan, on protocol 14. Spec: docs
rust-link/PROTOCOL.md §20.9.

- gc.started and gc.ended public, gc.failed staff's; guardedCrate.js reads
  each row per viewer (the clearer's name for the presence audience, Steam id,
  position and key for staff, `won` for everyone).
- Triggers rust.gc.started and rust.gc.ended (a crate a player cleared only).
- The step rust.gc.place: an event from rust.options.gc_events and a world
  call's location; refused at verify for Guarded Crate absent, NpcSpawn missing
  (D364), console unavailable, or an unknown event. Kept (D373); the run log's
  success line says so (D380). Budget rust.gc.crates.
- Warden (D375): guarded_won, title category guardedwon, Crates cleared column.
- Map marker, server settings line, swagger, engagement manifest, bundle, README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 12:26:50 -05:00
140bc67fe1 Merge pull request 'feat(rust): Dangerous Treasures — feed, map, Treasure Hunter, Start a Dangerous Treasures event (PLAN_REDESIGNS §14.3.4)' (#47) from feat/dangerous-treasures into edge
Reviewed-on: #47
2026-10-15 12:04:31 +00:00
bfe9b5b18d feat(rust): Dangerous Treasures — feed, map, Treasure Hunter, Start a Dangerous Treasures event (PLAN_REDESIGNS §14.3.4)
All checks were successful
PR Checks / client-build (pull_request) Successful in 27s
PR Checks / frozen-manifest (pull_request) Successful in 46s
PR Checks / server-tests (pull_request) Successful in 7m23s
Stage 5 of the §14.3 integration plan, on protocol 14 (docs
rust-link/PROTOCOL.md §20.8).

- dt.started and dt.won are public kinds, dt.ended a staff kind. The new
  dangerousTreasures.js reads each row per viewer: grid and invaderCount for
  everyone, the winner's name for the presence audience (D376), the position,
  the step's key, the winner's Steam id and the invaders for staff.
- Feed lines in the World events filter: "A Dangerous Treasures event has
  started · K12", "Alice won the treasure · K12 · 3 players fought for it".
- Triggers rust.dt.started (with byEvent) and rust.dt.won, public, cooldown
  subject the server, names only.
- eventDangerousTreasures.js: rust.dt.start, "Start a Dangerous Treasures
  event" (risk change, reversible none, budget rust.dt.events), server and
  difficulty. Refused at verify where the server said DT is not loaded or
  console.plugins is false (Rust-Plugins#34). dt-not-ready answers "Dangerous
  Treasures is not ready yet" with retryAfterMs 600000 (D365, D371);
  dt-no-spot retries on core's delay; DT's own refusals are permanent.
- Treasure Hunter (D375): treasuresWon -> treasures_won, the title category
  treasureswon, and a sortable Treasures column shown where the server
  reports DT or a row counts one.
- The map draws a running chest on the world layer; the server settings'
  plugin line names DT and whether a step can start one, from the
  integrations read's new `console`.
- coreApi ^1.15.0, ci/core-ref.json pinned to website main at #215's merge.
  ci/bundle.json ships the two new files; swagger fragment and engagement
  manifest regenerated; README lists DT.

Server 627/627 (16 new in test/dangerousTreasures.test.js) and every check;
client 92/92, build and externals. Booted against the pinned core: 45
triggers, 13 actions, 11 budgets, 21 option sources; 89 routes unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 06:45:30 -05:00
6802dbf5f9 Merge pull request 'feat(rust): Dynamic PVP's zones on the feed and the map (PLAN_REDESIGNS §14.3.3)' (#46) from feat/dynamic-pvp into edge
Reviewed-on: #46
2026-10-15 11:17:59 +00:00
549b3a5ffd feat(rust): Dynamic PVP's zones on the feed and the map (PLAN_REDESIGNS §14.3.3)
All checks were successful
PR Checks / client-build (pull_request) Successful in 25s
PR Checks / server-tests (pull_request) Successful in 31s
PR Checks / frozen-manifest (pull_request) Successful in 44s
Stage 4 of the §14.3 integration plan, on protocol 14 (docs
rust-link/PROTOCOL.md §20.7).

- pvp.zone.created is a public kind, pvp.zone.deleted a staff kind. Each row
  is read per viewer by the new dynamicPvp.js: grid for everyone, the event's
  name only for Dynamic PVP's own vanilla events, position and zone id for
  staff only.
- The feed's world filter carries the opening: "A PvP zone opened around the
  downed patrol helicopter · G7 · for 10 min". It names nobody.
- The map gains a fifth layer, `pvp` (PvP zones), public by default with its
  own audience in Admin → Rust visibility, drawn as a red ring with its
  event's name.
- Server settings' plugin line names Dynamic PVP; the swagger schemas and
  route descriptions carry the layer and the integration (fragment
  regenerated); ci/bundle.json ships dynamicPvp.js; README lists it.

Tests: server 611/611 (dynamicPvp.test.js new, catalogue and map extended)
and every check; client 89/89 (feed and map tooltip), build and externals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 06:12:07 -05:00
b5f7fa0f13 Merge pull request 'feat(rust): TruePVE in the zone step — no PvP protection, or a ruleset (PLAN_REDESIGNS §14.3.2)' (#45) from feat/truepve-zone into edge
Reviewed-on: #45
2026-10-15 11:06:11 +00:00
e3cc95f8ab feat(rust): TruePVE in the zone step — no PvP protection, or a ruleset (PLAN_REDESIGNS §14.3.2)
All checks were successful
PR Checks / client-build (pull_request) Successful in 24s
PR Checks / frozen-manifest (pull_request) Successful in 44s
PR Checks / server-tests (pull_request) Successful in 7m23s
Stage 3 of the §14.3 integration plan (D362, D368), on protocol 14 (docs
rust-link/PROTOCOL.md §20.6).

- rust.zone.open gains noPvpProtection (boolean) and pvpRuleset (string),
  sent as world.zone's truePve { exclude: true } or { ruleset }.
- Both set, a name over 64 characters, or a server whose hello says TruePVE
  is not loaded is refused for good, at verify too. A server that has not
  said is left to the plugin, as a dome is.
- truepve-missing and truepve-ruleset are permanent failures, with the
  plugin's words.
- The zone servers list carries each server's truePve from hello; Server
  settings' plugin line says whether TruePVE is loaded; the integrations
  schema documents it (swagger fragment regenerated).
- README lists TruePVE among the optional plugins.

Tests: five new in zones.test.js; server 603/603 and all checks, client
84/84 and the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 05:48:00 -05:00
4af731288a feat: TruePVE in the zone step (PLAN_REDESIGNS §14.3.2, WIP)
Stage 3 of the §14.3 integration plan (D362, D368): world.zone's optional
truePve ({exclude: true} or {ruleset}), all or nothing, unmapped on every
path that erases one of our zones, with truepve.json for erases while
TruePVE is away. Docs and PRs still to come.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 03:03:36 -05:00
176ded67a8 Merge pull request 'feat(rust): Rust's own world events — feed, credit rule, triggers, Call a world event (PLAN_REDESIGNS §14.3.1)' (#44) from feat/world-events into edge
Reviewed-on: #44
2026-10-15 07:19:10 +00:00
e7d5c8ed1a feat(rust): Rust's own world events — feed, credit rule, triggers, Call a world event (PLAN_REDESIGNS §14.3.1)
All checks were successful
PR Checks / client-build (pull_request) Successful in 31s
PR Checks / server-tests (pull_request) Successful in 36s
PR Checks / frozen-manifest (pull_request) Successful in -1m32s
The site half of stage 1, on protocol 14:

- server/worldEvents.js: the 24 types, D372's four credit rules, the grid
  from the map's geometry (none off the map), and the per-viewer reading.
- The feed serves `world.event` (a public kind): an unknown type is staff's;
  the event and its grid are public; names only for the presence audience
  (D376); Steam ids and positions only for staff (D353). A downed one carries
  `credited`/`creditedCount` by the server's rule. Client lines for all 24
  types and a World events filter.
- rust_servers.credit_rule (blow, last, most, all; default blow), set on the
  server's settings. Ingest counts heli_kills (patrol helicopters and
  Chinooks, D379) and apc_kills from `world.event` by that rule, so the line
  and Rotorbane / Tankbreaker agree. A protocol-13 tally's heliKills/apcKills
  still add as before.
- The tally's cratesHacked / dropsLooted → crates_hacked / drops_looted;
  title categories Safecracker and Scavenger (D356); leaderboard columns
  Crates hacked and Drops looted, sortable (D377).
- 24 engagement triggers rust.world.<event> (D355): ceiling everyone,
  cooldown subject the server and event (crates add where), names only.
- server/eventWorldCall.js: rust.world.call, "Call a world event"
  (risk change, reversible none, budget rust.world.calls). A position for an
  airdrop or a patrol helicopter only; the Chinook takes none (D378).

Server 585/585 (22 new in test/worldEvents.test.js), client 84/84, every
check; booted against the pinned core (43 triggers, 12 actions, 10 budgets,
20 option sources), routes.manifest unchanged at 89. Engagement manifest and
swagger fragment regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 00:39:00 -05:00
f65d96ebb6 Merge pull request 'chore(ci): pin the core to website main at #213's merge' (#43) from chore/core-ref-213 into edge
Reviewed-on: #43
2026-10-14 09:35:21 +00:00
11d32436a4 chore(ci): pin the core to website main at #213's merge
All checks were successful
PR Checks / client-build (pull_request) Successful in 28s
PR Checks / server-tests (pull_request) Successful in 37s
PR Checks / frozen-manifest (pull_request) Successful in -1m34s
website#213 (MODULE_API 1.14.0, the `multiple` param) merged as a981b2b.
Its tree is identical to the branch head 743f1bc this was pinned to, so
routes.manifest.json is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-14 04:18:53 -05:00
aefd752372 Merge pull request 'feat(rust): permissions as event rewards — lend or give any registered permission (PLAN_REDESIGNS §12)' (#42) from feat/rust-perm-rewards into edge
Reviewed-on: #42
2026-10-13 01:03:39 +00:00
63c82d5a2f feat(rust): permissions as event rewards — lend or give any registered permission (PLAN_REDESIGNS §12)
All checks were successful
PR Checks / client-build (pull_request) Successful in 20s
PR Checks / server-tests (pull_request) Successful in 27s
PR Checks / frozen-manifest (pull_request) Successful in -1m32s
D342-D346 (docs#342). Two steps over the permission mirror's own tables, so
no plugin, sidecar or protocol change:

- rust.perm.lend, "Permissions until the event ends": a kit reward's lease,
  one rust_perm_run_grants row per recipient per permission, the message on
  one row each; change/ledger with rust.kit.entitle's revert and reconcile;
  budget rust.permissions.
- rust.perm.give, "Permissions for good": a standing rust_perm_grants row per
  website user per permission, scoped to the step's server, source `event`,
  note "event run <run>, step <step>"; a grant already there is left alone,
  and a retry's own grants are counted as given; the message rides a
  notice-only run-grant row; irreversible/none; budget rust.permissions.kept.
- Both: several permissions through core's `multiple` param (MODULE_API
  1.14.0, at most 10); a required "I am sure" toggle refused unticked in the
  dry run and at run time, naming the permissions (D343); a permission the
  server's catalogue lacks refused by name; a blank message tells nobody.
- rust.options.permissions: the enabled servers' catalogues, grouped by the
  registering plugin (by prefix when none), sorted plugin then permission
  without case, a row noted "Only on X" when not on every server.
- Permission manager: an event-given grant shows "given by event run N,
  step M"; staff turn it off like any grant.
- coreApi ^1.14.0; ci/core-ref.json pinned to website#213's head until it
  merges; engagement-triggers.json regenerated; routes.manifest.json
  unchanged (no new routes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-12 15:49:44 -05:00
89557ddf66 Merge pull request 'feat(rust): Backpacks — staff and player views, the Bigger backpack step (PLAN_REDESIGNS §11.3)' (#41) from feat/rust-backpacks into edge
Reviewed-on: #41
2026-10-12 19:41:20 +00:00
e4591af906 feat(rust): Backpacks — staff and player views, the Bigger backpack step (PLAN_REDESIGNS §11.3)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 37s
PR Checks / frozen-manifest (pull_request) Successful in 1m0s
PR Checks / client-build (pull_request) Successful in 7m23s
The site half of §11.3.2, on protocol 14:

- `rust.backpack.size` ("Bigger backpack"): a kit reward with a different
  permission (D337) — one rust_perm_run_grants row per website user with
  backpacks.size.N, no kit, no credit; the perm mirror pushes it and the run's
  revert deletes it. risk change, reversible ledger, a new `rust.backpacks`
  budget, rust.kit.entitle's recipient modes including `linked`. Refused with
  "Backpacks is not installed on this server" in the dry run too (D199), and a
  size the server does not offer is refused by name. Its message is never
  silent: empty is the take-back warning (D338).
- `rust.options.backpacksizes`: each server's sizes above its smallest, from
  the last hello, grouped by server.
- GET /player/rust/backpacks (own links only, 20 reads a minute) and
  GET /admin/users/:id/rust/backpacks (that user's links only). Live, never
  stored; a stranger's id is a 404 and never reaches the game.
- Client: a shared Backpack panel (pick server and account, open, read once)
  on the account page and the admin user page; the grid draws pages of
  pageSize, each slot named by the game (D340, no icons), blueprints by what
  they make (D341), overflow marked. Backpacks joins the server's mod line.

Server 548/548 (13 new in test/backpacks.test.js), client 79/79 (5 new),
routes.manifest regenerated against the pinned core (89 routes), swagger
fragment regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-12 14:35:55 -05:00
69ec0f6c46 Merge pull request 'feat(rust): Economics — pay coins, balances, staff adjust, the richest (PLAN_REDESIGNS §11.2)' (#40) from feat/protocol-14-economics into edge
Reviewed-on: #40
2026-10-12 12:40:53 +00:00
845caef817 feat(rust): Economics — pay coins, balances, staff adjust, the richest (PLAN_REDESIGNS §11.2)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 31s
PR Checks / client-build (pull_request) Successful in 29s
PR Checks / frozen-manifest (pull_request) Successful in -1m29s
The site half of §11.2.2, on protocol 14:

- `rust.coins.pay` ("Pay coins"): irreversible, reversible none (D335). The
  recipient modes of rust.kit.entitle including `linked`, one payment per
  website user (D103's rule), an optional message with {amount}, a new
  `rust.coins` budget. Refused with "Economics is not installed on this
  server" in the dry run too (D199). Core's key goes to the plugin, so a
  retried step answers `repeat` and pays nobody twice.
- `rust_player_wipe_stats.balance`: the tally's balance, written as a value.
  A nameless balance frame (an offline player) moves nobody's last-seen.
  `economics.wiped` clears the wipe's balances, except at a new save.
- The richest: a Coins leaderboard column on one wipe of a server that
  reports Economics, `sort=balance`, and a `richest` title category
  (Magnate); both rank only above the starting balance (D336).
- GET /player/rust/economics (live, falling back to the last known), and
  GET/POST /admin/users/:id/rust/economics for staff — scoped to the user's
  own links, keyed per submission, an activity row with before and after.
- Client: the account page's Coins, the admin user page's Coins panel with
  its form, the leaderboard column, and Economics in the server's mod line
  (saying when its config could not be read).

Server 535/535 (14 new in test/economics.test.js), client 74/74,
routes.manifest regenerated against the pinned core (87 routes), swagger
fragment regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-12 07:07:05 -05:00
1777cb6ce1 Merge pull request 'chore(ci): re-pin core to website main at #212's merge' (#39) from chore/repin-core-212 into edge
Reviewed-on: #39
2026-10-12 11:41:31 +00:00
a64ae68158 chore(ci): re-pin core to website main at #212's merge
All checks were successful
PR Checks / server-tests (pull_request) Successful in 33s
PR Checks / frozen-manifest (pull_request) Successful in 55s
PR Checks / client-build (pull_request) Successful in 7m23s
website#212 (MODULE_API 1.13.0) merged as ca7ec53, whose tree is the
same as the branch head e58857c this repo was pinned to while it was
open. Only the pin moves; routes.manifest.json is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-12 05:08:39 -05:00
21e40053ab Merge pull request 'feat(rust): the kit weekend, linked rewards, reward messages and the kit warning (protocol 14)' (#38) from feat/protocol-14-step-editor into edge
Reviewed-on: #38
2026-10-12 09:51:57 +00:00
d21a9e2c0d feat(rust): the kit weekend, linked rewards, reward messages and the kit warning (protocol 14)
All checks were successful
PR Checks / client-build (pull_request) Successful in 3m46s
PR Checks / frozen-manifest (pull_request) Successful in 6m12s
PR Checks / server-tests (pull_request) Successful in 7m30s
PLAN_REDESIGNS §2, the Rust half (D164, D192, D332, D333):
- rust.template.kit_weekend (core registerEventTemplates, MODULE_API 1.13.0):
  inputs kit/minutes/message/delivery; one phase advance after the weekend,
  core.lease rust.group.permission default/<kit permission> + rust.kit.entitle
  linked. Steps only where the kit can use them; validated by the real core.
- rust.kit.entitle: recipients 'linked' (every linked player who has played
  on the server, no tally, refused past the bound), optional message ({kit})
  and delivery, stored per row (rust_perm_run_grants.notice*).
- perm.sync notices: the untold set to the picked account, keyed
  run:step:user, in the digest; the report's noticesTold marks them told.
- rust.options.grouppermissions notes a kit's permission rows via kits.uses
  (cached a minute per server; a failure is no note).
- duration: 'minutes' on rust.zone.open and rust.participation.open; the
  group-permission lease's value example is true.
- protocol 13 -> 14, coreApi ^1.13.0; ci/core-ref.json pinned to website#212's
  head while it is open; engagement-triggers.json regenerated (coreApi line).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-12 02:25:06 -05:00
be8c09e6b3 Merge pull request 'feat(rust): RunicNPC required, protocol 13, edge → main (runicnpc 9e step 3, merge 3 of 3)' (#37) from edge into main
All checks were successful
Release / release (push) Successful in -1m42s
Reviewed-on: #37
v0.4.0
2026-10-09 07:22:41 +00:00
65cef3e7c0 Merge pull request 'feat(zones): a blank dome stack is 3 spheres, not 1 (runicnpc 9e, D326)' (#36) from feat/9e-dome-stack-3 into edge
All checks were successful
PR Checks / server-tests (pull_request) Successful in 36s
PR Checks / frozen-manifest (pull_request) Successful in 1m1s
PR Checks / client-build (pull_request) Successful in 7m27s
Reviewed-on: #36
2026-10-09 06:29:26 +00:00
e1789c1df4 feat(zones): a blank dome stack is 3 spheres, not 1 (runicnpc 9e, D326)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 32s
PR Checks / frozen-manifest (pull_request) Successful in 1m13s
PR Checks / client-build (pull_request) Successful in 7m49s
The org lead chose 3 by eye at noon in RunicNPC's 9d player session. The
zone step sends it when its stack is left blank, and the field's hint says
so. The wire is unchanged: the module always sends a stack, and the bridge's
own default for a dome without one stays 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-09 01:24:19 -05:00
4ad38d443b Merge pull request 'fix(swagger): regenerate the fragment for stage 9's runicNpc status field' (#35) from fix/swagger-fragment-stage9 into edge
Reviewed-on: #35
2026-10-07 02:47:36 +00:00
8b6e96086f fix(swagger): regenerate the fragment for stage 9's runicNpc status field
Some checks failed
PR Checks / client-build (pull_request) Successful in 17s
PR Checks / frozen-manifest (pull_request) Failing after 32s
PR Checks / server-tests (pull_request) Successful in 7m32s
RunicNPC stage 9 (#34, D310) added a runicNpc object to the server status
schema in server/swagger/doc.js but did not regenerate swagger-fragment.json,
so check:swagger turned server-tests red on run 38 and edge has been red
since the merge. This is the output of `npm run swagger`, nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 21:46:56 -05:00
1188a3142e Merge pull request 'feat(rust): RunicNPC is required for every Place NPCs step (runicnpc stage 9, D310)' (#34) from feat/runicnpc-stage9 into edge
Reviewed-on: #34
2026-10-07 02:43:32 +00:00
f71a2907fb docs(rust): the NPC picker's comment says what stage 9 kept (D310)
Some checks failed
PR Checks / client-build (pull_request) Successful in 23s
PR Checks / server-tests (pull_request) Failing after 26s
PR Checks / frozen-manifest (pull_request) Successful in 42s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 21:34:01 -05:00
d140d5c66d feat(rust): RunicNPC is required for every Place NPCs step (runicnpc stage 9, D310)
A Place NPCs step on a server without RunicNPC, or with one older than the
bridge needs, is refused before it is sent: "<server> needs RunicNPC to place
NPCs, Rust's own scientists included: <why>". Rust's own scientists were the
fallback until now (D243); they need RunicNPC too. Crates are unaffected.

- npcs.model: API_NEEDED rises from 4 to 6, the bridge's RunicNpcApiNeeded, so a
  server the site calls ready is one the bridge will not refuse as runicnpc-old.
  The profile push follows the same floor.
- eventWorld: profileMissing becomes npcMissing, asked for every NPC step.
- Admin → Rust → Servers: each server carries `runicNpc` ({ready, absence,
  version, api}) and a server without one that will do reads "Incomplete" with
  the reason. Swagger documents the field.

The event picker is unchanged in this commit (an open question on the PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 16:58:30 -05:00
72f6c557d2 Merge pull request 'feat(rust): event NPCs named on the map, and a Place NPCs step's progress line (runicnpc stage 8)' (#33) from feat/runicnpc-stage8 into edge
Reviewed-on: #33
2026-10-06 13:32:43 +00:00
5162496d74 feat(rust): event NPCs named on the map, and a Place NPCs step's progress line (runicnpc stage 8)
All checks were successful
PR Checks / client-build (pull_request) Successful in 20s
PR Checks / server-tests (pull_request) Successful in 25s
PR Checks / frozen-manifest (pull_request) Successful in -1m18s
- rust.npc.place's progress() (MODULE_API 1.12.0, D304). It reports the
  step's NPCs still standing, by its key, of how many it placed, with
  the profile's label: "Bandit: 2 of 3 left". A boss step is its
  health, "Walk Juggernaut: 59%", and a boss's adds are not counted.
  The data comes from the map's live answer, so it costs the game no
  extra ask. A reader who may not see the map's events layer gets no
  line.
- The map (D302, D303):
  - an NPC's tooltip is its name and its event's title, read from
    core's public calendar, which lists only announced events;
  - a boss is bigger and in its own colour, and an add says so;
  - a boss outside any event is in the world layer;
  - a boss's health is removed from what a viewer is sent.
- The world layer's visibility hint mentions those bosses.

coreApi stays ^1.11.0: an older core ignores the optional member, and
the action works without a progress line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 08:23:13 -05:00
3c3d154b36 Merge pull request 'feat(rust): loot on the NPC profile form, and the Place NPCs step's "drop loot" switch (runicnpc stage 7)' (#32) from feat/runicnpc-stage7 into edge
Reviewed-on: #32
2026-10-06 07:16:44 +00:00
34ca84f6be fix(rust): the profile list says "own loot" for a profile with a loot table
All checks were successful
PR Checks / server-tests (pull_request) Successful in 27s
PR Checks / frozen-manifest (pull_request) Successful in 49s
PR Checks / client-build (pull_request) Successful in 7m42s
Found on the stage 7 walk: a profile with its own loot read exactly like
one without in the list under the form, where a boss already says
"boss".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 01:28:18 -05:00
33c5178ef9 feat(rust): loot on the NPC profile form, and the Place NPCs step's "drop loot" switch (runicnpc stage 7)
The profile form's Loot section (D290-D301):

- what the corpse starts with (Rust's scientist loot, the kit, or
  nothing);
- rows that always roll, each with its chance;
- a pool capped at its picks, with a "nothing" weight;
- which crate the table goes into, and a locked crate's hack time;
- how long the corpse stays.

The site checks the block as RunicNPC does, in its words. Whether an
item exists on a server is RunicNPC's to say on the push.

The Place NPCs step's dropLoot switch (D296), on by default and sent
only when off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-06 00:32:24 -05:00
bb3e25b5d7 Merge pull request 'feat(rust): bosses and passive NPCs on the profile form, and public boss triggers (runicnpc stage 6)' (#31) from feat/runicnpc-stage6 into edge
Reviewed-on: #31
2026-10-06 00:45:19 +00:00
f2ca314d10 feat(rust): bosses and passive NPCs on the profile form, and public boss triggers (runicnpc stage 6)
All checks were successful
PR Checks / client-build (pull_request) Successful in 20s
PR Checks / server-tests (pull_request) Successful in 24s
PR Checks / frozen-manifest (pull_request) Successful in 46s
The profile form's boss box (D273-D277): the bar's distance, where its
lines are said, the spawn and death lines, and phases at health
percentages, each with optional damage, aim, speed, ranges, a kit swap,
adds of another profile and a line. The passive role (D278, D279,
D288): press E answers with a chat line or a window, it moves as its
profile says (now including `stand`), and it starts unhurtable.

The site checks both as RunicNPC does, in its words; a phase's kit is
checked on each server like the profile's own, and its adds must name a
site profile that is not a boss.

Public triggers rust.boss.spawned, rust.boss.phase and rust.boss.killed
(D277) from the bridge's new frames. In the feed, a boss appearing is
public; its death names the killer, so it sits behind the presence
setting; its phases are staff's, as the game tells them only to players
near it (D286). engagement-triggers.json regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-05 10:54:28 -05:00
620ea63b54 Merge pull request 'feat(rust): NPC sides, the faction table, an event's escort, ally and tether (runicnpc stage 5)' (#30) from feat/runicnpc-stage5 into edge
Reviewed-on: #30
2026-10-05 08:38:43 +00:00
11619a4dc6 feat(rust): NPC sides, the faction table, an event's escort, ally and tether (runicnpc stage 5)
All checks were successful
PR Checks / client-build (pull_request) Successful in 31s
PR Checks / server-tests (pull_request) Successful in 39s
PR Checks / frozen-manifest (pull_request) Successful in 58s
RunicNPC stage 5 on the site (docs runicnpc/PLAN.md, D253-D272):

- profiles gain the guard role, faction, relations (its own exceptions),
  alertRadius, turrets, hurtByPlayers, hurtsPlayers and kitUse, checked in
  RunicNPC's order and words, and defaulting to "players only" (D255);
- the faction table: one site-wide table, one row per pair and both ways
  (D254, D268), stored in rust_npc_factions, edited on the NPC profiles page
  (PUT /admin/rust/npcs/factions), pushed to every server with its
  profiles and hashed with them, and a standalone server's own pairs
  adopted at its first push with the site's winning (D244, D251);
- the Place NPCs step takes escort (a Steam id or a {placeholder}), an ally
  (a clan from the new rust.options.clans source, or the team of a player)
  and tether (the zone this event made), for a RunicNPC profile only
  (D269, D270, D272);
- a placement may be held inside a zone (tether, D272);
- the bridge's RunicNPC API floor is 4.

Tests: 488 server, 66 client. routes.manifest.json regenerated against the
pinned core (one route added).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-01 07:58:13 -05:00