fix(rust): protocol 13 — a configuration save that settles after its reload (F9, D179)
The plugin now answers a save once the files are written, with pending and a writeId, and reports the reload later as a config.outcome event. The save is recorded as reloading with a settle_by of two plugin ceilings plus slack on the database's clock; ingest settles the row by (server, writeId), only while it is still reloading, so a replay moves nothing and a late outcome still lands. A row past settle_by reads as lost. GET /admin/rust/config/:serverId/writes/:writeId serves the poll; the page polls it every two seconds, holds the Save button while it waits, and says whether a rolled-back plugin came back on its old file. config.outcome is a staff kind: it carries the server's log tail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -41,7 +41,7 @@
|
||||
"Admin · Rust"
|
||||
],
|
||||
"summary": "Save a configuration file and reload its plugin",
|
||||
"description": "Send `edits` (the generated form: pointers and literals, type-preserving) or `text` (the raw tier: the whole document). `version` must match what the host holds or the save is refused 409 with the current file. The game backs the file up, writes it, reloads the named plugin, and **restores the old file automatically** if the plugin does not come back — which is answered 200 with `report.rolledBack`, because a rollback is a round trip that worked and an edit that did not.",
|
||||
"description": "Send `edits` (the generated form: pointers and literals, type-preserving) or `text` (the raw tier: the whole document). `version` must match what the host holds or the save is refused 409 with the current file. The game backs the file up, writes it, reloads the named plugin, and **restores the old file automatically** if the plugin fails to load. With a plugin to reload, the answer comes as soon as the file is written — `pending: true` and `write.id` — and the outcome is read from `GET …/writes/{writeId}` once the reload settles. Without one, the answer is final.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "serverId",
|
||||
@@ -54,7 +54,7 @@
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "What happened: applied, or rolled back with the reason"
|
||||
"description": "Written and reloading (`pending`, with the write’s id), or the final outcome when nothing was reloaded"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid body, an edit the form may not make, or a locked key"
|
||||
@@ -158,6 +158,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/rust/config/{serverId}/writes/{writeId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Admin · Rust"
|
||||
],
|
||||
"summary": "One configuration write, while its reload settles",
|
||||
"description": "A save that reloads a plugin answers as soon as the file is written, with `pending: true` and this write’s id; the reload can take longer than a request should, behind a cold compile. Poll this until `outcome` leaves `reloading`: `applied`, or `rolled-back` with the reason, the server’s log, and `restored` (whether the plugin came back on its old file). `lost` means the plugin never reported — it was reloaded, or the link dropped — so re-read the file.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "serverId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "writeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The write and how far it has got"
|
||||
},
|
||||
"404": {
|
||||
"description": "No such write on that server"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/rust/permissions": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user