The reports desk: a slip for surprises, replies in the hall
A Report button in a room's masthead files what happened and what was expected, with a screenshot if there is one, pinned to the room and the turn and rung through Sentry. Reports and the keeper's replies live in feedback.jsonl beside the ledgers; the hall shows each seat's reports with the thread under them and a line to answer. pull-reports.sh mirrors the desk locally, report-reply.sh answers, and a skill works the desk. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
This commit is contained in:
1 parent
c49703ee4f
commit
8eca078043
11 files changed
+738
-5
No files matched your search
+23
-1
@@ -68,6 +68,20 @@ async function call<T>(method: string, path: string, body?: unknown): Promise<T>
|
||||
|
||||
type Seated = { seat: WizardId; token: string; view: RoomView };
|
||||
|
||||
/** A report to the keeper, with the exchange under it. */
|
||||
export interface Report {
|
||||
id: string;
|
||||
at: string;
|
||||
roomId: string;
|
||||
player: string;
|
||||
seat: WizardId | null;
|
||||
turn: number | null;
|
||||
happened: string;
|
||||
expected: string;
|
||||
thread: { at: string; text: string; from: 'desk' | 'player'; status?: string }[];
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
create: (name: string, size: number) => call<Seated>('POST', '/api/rooms', { name, size }),
|
||||
join: (roomId: string, name: string) => call<Seated>('POST', `/api/rooms/${roomId}/join`, { name }),
|
||||
@@ -77,7 +91,15 @@ export const api = {
|
||||
/** The gallery's view: no token, no seat, nothing any wizard could not see. */
|
||||
watch: (roomId: string) => call<RoomView>('GET', `/api/rooms/${roomId}`),
|
||||
turn: (roomId: string, token: string, input: TurnInput) => call<RoomView>('POST', `/api/rooms/${roomId}/turn`, { token, input }),
|
||||
say: (roomId: string, token: string, text: string) => call<RoomView>('POST', `/api/rooms/${roomId}/say`, { token, text })
|
||||
say: (roomId: string, token: string, text: string) => call<RoomView>('POST', `/api/rooms/${roomId}/say`, { token, text }),
|
||||
report: (roomId: string, token: string, happened: string, expected: string) =>
|
||||
call<{ id: string }>('POST', `/api/rooms/${roomId}/report`, { token, happened, expected }),
|
||||
reportImage: async (id: string, file: File) => {
|
||||
const res = await fetch(`/api/reports/${id}/image`, { method: 'POST', headers: { 'content-type': file.type || 'application/octet-stream' }, body: file });
|
||||
if (!res.ok) throw new ServerError(((await res.json().catch(() => ({}))) as { error?: string }).error ?? `The server answered ${res.status}.`, res.status);
|
||||
},
|
||||
myReports: (seats: { roomId: string; token: string }[]) => call<{ reports: Report[] }>('POST', '/api/reports/mine', { seats }),
|
||||
answerReport: (id: string, roomId: string, token: string, text: string) => call<{ ok: true }>('POST', `/api/reports/${id}/answer`, { roomId, token, text })
|
||||
};
|
||||
|
||||
/** A socket that only ever says "fetch the view again"; reconnects on its own. An empty token sits in the gallery. */
|
||||
|
||||
Reference in new issue
Block a user