From the hnefatafl repo's pass of the same day, everything that touched a kit-shared file. The visitors digest and the nightly rollup share deploy/traffic.py, installed to /usr/local/lib/<slug>; the rollup writes the finished-games count it computed behind "and False". pull-reports.sh and the reports skill both use deploy/report-digest.ts. The seat line requires its token hash and the start line its rules revision; the migration for ledgers written before hashing goes with them. The route table in server/src/index.ts lists every route; Report, ReportLine and Tally are declared once in view.ts for both sides; exports nobody imported are exports no more. In the client: .small, the × that dismisses, and the frame of the reading pages are in app.css once; the preferences panel shares the report slip's modal shape; the room store gains seatEmpty and seatUnheld, and the lobby and the join page read those instead of three spellings of their own. The room's moved and awaiting fields stay: the demo board reads them, and simultaneous rounds are the contract. The deploy README no longer describes a browser-only game; the visitors skill no longer names a /play route; the reports skill's replay call carries the room's options. The Slack channel id is passed in the environment rather than filled in as a placeholder. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwFKMuQnPAEHJ5yA1q4orh
33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
// The reports digest: every report with its exchange, newest first, written
|
|
// as reports.md beside a mirrored feedback.jsonl. Used by deploy/pull-reports.sh.
|
|
// tsx deploy/report-digest.ts <folder>
|
|
|
|
import { writeFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { Reports } from '../server/src/reports';
|
|
|
|
const dir = process.argv[2];
|
|
if (!dir) {
|
|
console.error('usage: report-digest.ts <folder>');
|
|
process.exit(2);
|
|
}
|
|
|
|
const when = (iso: string) => iso.slice(0, 16).replace('T', ' ');
|
|
const reports = new Reports(dir).read().reverse();
|
|
const lines = ['# __NAME__ reports', '', `${reports.length} reports; newest first. Pictures are in \`images/\`.`, ''];
|
|
for (const r of reports) {
|
|
lines.push(`## ${when(r.at)} — ${r.player} in ${r.roomId} (turn ${r.turn ?? '?'}, seq ${r.seq}) — id ${r.id}`, '');
|
|
lines.push(`**What happened:** ${r.happened.trim()}`);
|
|
if (r.expected.trim()) lines.push(`**What they expected:** ${r.expected.trim()}`);
|
|
if (r.image) lines.push(`**Picture:** `);
|
|
for (const line of r.thread) {
|
|
const who = line.from === 'player' ? `${r.player} answers` : `**${line.status}**`;
|
|
lines.push(`> ${who} (${when(line.at)}): ${line.text.trim()}`);
|
|
}
|
|
const last = r.thread[r.thread.length - 1];
|
|
if (!last || last.from === 'player') lines.push('> _awaiting the keeper_');
|
|
lines.push('');
|
|
}
|
|
writeFileSync(join(dir, 'reports.md'), lines.join('\n'));
|
|
console.log(`${reports.length} reports, ${reports.filter((r) => r.image).length} with pictures -> ${join(dir, 'reports.md')}`);
|