Diagnose stalled administrator decisions from the operations page

Add an owner-only, read-only sample of at most ten live rooms whose public
pending interaction — decision, response, Truthtrance or phase opening — has
been idle longest, with the room code, idle duration, interaction kind, turn
and phase. GET /api/admin/operations?stalled=1 repeats live owner authority,
answers no-store, rejects any other query combination and repairs nothing;
paused, closed, removed and archived rooms are excluded and no private game
content is selected. The aggregate sample adds stalledRooms and
oldestStalledChange for the same predicate.

Owner-only unit coverage asserts the counters, the oldest stalled write, the
bounded oldest-first order, every exclusion boundary, idle arithmetic, the
absence of private content and the owner recheck after demotion. The anonymous
HTTP boundary denies the new read, and the full HTTP integration suite passes
55/55 against a local build of this revision. The panel was verified in the
real browser against six hours of genuine idle rooms, which caught and fixed a
missing threshold field in the response.

No game, seat, saved state, deployment, public start or played-save conversion
changes.
This commit is contained in:
Quintine committed 2026-10-07 17:51:08 +11:00
1 parent e05a205a47
commit 07c20ae806
10 files changed
+287 -11

No files matched your search

+11 -2
View File
@@ -15,7 +15,7 @@ recovery and persistence systems where suitable.
| Lifecycle and removal | [Pause/resume and joining locks](ADMIN_ROOM_CONTROLS.md), [administrative close/reopen](ADMIN_ROOM_CLOSURE.md) and [recoverable removal/restoration](ADMIN_ROOM_REMOVAL.md) have controls, durable audit, exact retries and player/AI enforcement. [Archive/unarchive](ADMIN_ROOM_ARCHIVE.md) has separate directory filters and exact retries. Permanent deletion and bulk actions remain missing. | Prototyped, partial |
| Participant support | [Participant AI](ADMIN_PARTICIPANT_AI.md) enables an existing difficulty on an eligible human seat in a paused game, retaining access/takeback and private custody. [Discussion controls](ADMIN_DISCUSSION.md) mute/unmute new sends while retaining history and gameplay. Participant removal, access revocation, assisted recovery and further replacement remain. Existing lobby controls can reassign the host. | Prototyped, partial |
| Saved-game operations | [Owner-only room backups](ADMIN_BACKUPS.md) capture/list/download a size-limited, version-fenced room snapshot without changing play or access; import validation, safe checkpoint restore and interrupted-work diagnosis/resume remain missing. | Prototyped, partial |
| Operations and audit | [Action history](ADMIN_ACTION_HISTORY.md) searches existing durable operations with safe settings and role-limited reasons. The owner-only [operations page](../app/admin/operations/page.tsx) samples build revision, room/seat flags, JSON syntax failures and backup/export counts without exposing game contents. An explicit owner action runs read-only SQLite quick/foreign-key checks and returns only pass/fail, never corrupt row details. Failed-attempt records, disk capacity, stalled-decision diagnosis and maintenance controls remain. | Prototyped, partial |
| Operations and audit | [Action history](ADMIN_ACTION_HISTORY.md) searches existing durable operations with safe settings and role-limited reasons. The owner-only [operations page](../app/admin/operations/page.tsx) samples build revision, room/seat flags, JSON syntax failures and backup/export counts without exposing game contents. [Stalled-decision diagnosis](ADMIN_STALLED_DECISIONS.md) adds a bounded, read-only sample of live rooms whose public pending interaction has been idle longest, with aggregate counters. An explicit owner action runs read-only SQLite quick/foreign-key checks and returns only pass/fail, never corrupt row details. Failed-attempt records, disk capacity and maintenance controls remain. | Prototyped, partial |
Track each area through Missing, Prototyped, Integrated, Verified and Polished.
As implementation begins, link its controls, server actions, persistence,
@@ -82,7 +82,16 @@ owner authority inside the query batch and returns only pass/fail and sample
time, never a table name, row ID, private save or SQLite diagnostic text.
The check can take time; a failed check calls for a protected snapshot and
private diagnosis, not automatic repair. It does not prove disk capacity,
backup recoverability, valid game rules or the absence of stalled decisions.
backup recoverability or valid game rules.
[Stalled-decision diagnosis](ADMIN_STALLED_DECISIONS.md) adds **List stalled
decisions**: a bounded, read-only sample of at most ten live rooms whose public
pending interaction — a decision, response, Truthtrance or phase opening — has
been idle longest, with the room code, idle duration, interaction kind, turn
and phase. Paused, closed, removed and archived rooms are excluded, no private
game content is selected, and the sample repairs nothing; the aggregate sample
also reports the stalled count and the oldest stalled write. A stall is inferred
from the room write time, so a slow human turn counts too.
The directory uses an explicit field allowlist, independent of any player view.
It never returns hands, Traitors, predictions, plans, private messages, recovery
+48
View File
@@ -0,0 +1,48 @@
# Stalled-decision diagnosis
Added 7 October 2026 as owner-only, read-only administration scope. It samples
rooms that are waiting on a public interaction and have not been written for a
while, so an operator can find a table that stopped moving without opening
anyone's private state. It changes nothing and repairs nothing.
## Contract
`GET /api/admin/operations?stalled=1` requires a live owner session, checks the
`X-Dune-Admin-Id` header against the session, and answers `no-store`. Any other
query combination is rejected with `400`; an anonymous or non-owner request is
rejected with `401`/`403` before any room is read. The same request without a
query returns the aggregate sample, and `?integrity=1` keeps its manual SQLite
check.
A room is included only when **all** of these hold:
- its saved state is readable JSON (`json_valid(state)=1`);
- `status` is `playing`;
- it has a pending public interaction: `decision`, `response`, `truthtrance`
or `phaseOpening`;
- it is not paused, closed, removed or archived;
- its last room write is older than `ADMIN_STALL_MS` (six hours).
The sample returns at most `ADMIN_STALL_LIMIT` (ten) rooms, oldest first, with
the room code, the idle duration, the pending interaction kind and the saved
turn and phase. The aggregate operations sample adds `stalledRooms` and
`oldestStalledChange` for the same predicate.
## Privacy and safety
Only public coordination facts leave the database: a room code is an
invitation, and a pending interaction kind, turn and phase are already visible
to the players at that table. Hands, spice, orders, private decisions, seat
credentials and recovery material are never selected, so the sample cannot
leak them. The route is read-only: it runs no repair, no automatic close, no
retry and no migration. Pausing, resuming, closing, removing or archiving a
room still happens only through its own audited control.
## Limits
A stall is inferred from the room write time, so a genuinely slow human turn, a
long offline pause that was never flagged, or an idle-but-live table all count.
The sample does not diagnose *why* a decision is stuck, does not check disk
capacity or backup recoverability, and does not verify rule-level save validity.
It complements, and does not replace, the manual integrity check and the
[action history](ADMIN_ACTION_HISTORY.md).
+10
View File
@@ -50,6 +50,16 @@ settles, without replaying payment or force transfer. A Terror entry already in
flight and every Ambassador combination still reject. Selected decks/cache,
shared-Duke/capture, force/payment and pending-effect guards remain.
**In progress — administrator operations.** The owner-only operations page now
also diagnoses [stalled decisions](ADMIN_STALLED_DECISIONS.md): a bounded,
read-only sample of at most ten live rooms whose public pending interaction — a
decision, response, Truthtrance or phase opening — has been idle longest, plus
`stalledRooms` and `oldestStalledChange` counters in the aggregate sample.
Paused, closed, removed and archived rooms are excluded and no private game
content is selected; `GET /api/admin/operations?stalled=1` stays owner-only,
`no-store` and read-only, and it repairs nothing. Failed-attempt records, disk
capacity and maintenance controls remain.
**Mixed E1/E2 and paired E3 Nexus — bounded Development Verified.**
Original both-family Ix+CHOAM/union47 or both Ecaz+Moritani/ecaz33/classic
+26
View File
@@ -1,5 +1,31 @@
# Dune implementation status
## 7 October 2026 — administrator stalled-decision diagnosis
The owner-only operations page gains **List stalled decisions**: a bounded,
read-only sample of at most ten live rooms whose public pending interaction —
`decision`, `response`, `truthtrance` or `phaseOpening` — has been idle longest,
reported as room code, idle duration, interaction kind, turn and phase. The
aggregate sample adds `stalledRooms` and `oldestStalledChange` for the same
predicate. `GET /api/admin/operations?stalled=1` repeats live owner authority,
answers `no-store`, rejects any other query combination with `400` and stays
read-only: it repairs nothing and pause/close/remove/archive keep their own
audited controls.
Paused, closed, removed and archived rooms are excluded, as are unreadable JSON
states and non-playing rooms. Only public coordination facts are selected — no
hands, orders, private decisions, seat credentials or raw saved state — and the
sample is bounded to ten rows, oldest first. A stall is inferred from the room
write time, so a slow human turn also counts; the sample does not diagnose why a
decision is stuck, disk capacity or backup recoverability.
Owner-only unit coverage asserts the counters, the oldest stalled write, the
bounded oldest-first order, every exclusion boundary, idle arithmetic, the
absence of private content and the owner recheck after demotion. The anonymous
HTTP boundary now also denies `?stalled=1`; the full HTTP integration suite
passes 55/55 against a local build of this revision. No game, seat, saved state,
deployment, public start or played-save conversion changes.
## 7 October 2026 — mixed E3 programme evidence and the deferred Terror entry
The withdrawn mixed E3 programme pairs are green: Moritani9/9 and Ecaz14/14