feat(admin): correct published match report stats - #269
Conversation
Approval is terminal by design, so a completed report was read-only and a misread scoreboard could not be repaired from the admin screen. - POST /api/admin/match-reports/[id]/correct calls the new correct_match_report_result RPC. It requires every player to be linked, carries the revision the admin loaded and a per-attempt correction key, and passes `applied` through so a replayed key is reported as "already recorded" rather than a fresh save. A superseded revision or a reused key maps onto 409. - The published view gains a deliberate correction mode: it asks for a reason, turns the read-only editors editable against the real roster, and re-reads the report on cancel so abandoned edits do not linger. Does not typecheck until db-contract.lock.json is bumped to the release carrying the RPC; the tag does not exist yet, so that is the next commit.
Bumps db-contract.lock.json from db-v1.20.0 to db-v1.21.1 and vendors the matching generated types, which add correct_match_report_result. This is what the admin correction route calls, so the route now typechecks. The pinned commit is 4bf9f34e (current sal-database main) rather than the merge commit of the contract bump itself: contract.json and generated/database.types.ts are byte-identical at both commits, and the release workflow requires the released commit to be main's head. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bs5rcUV4wybANpywoyaMAB
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: da704851c6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
CI blocker:
|
The server page loader and GET /api/admin/match-reports each kept their own copy of the match_reports row mapping, and the two had drifted: - the loader dropped `revision`, so `expectedRevision` fell back to 1 on a fresh page load and any report past its first revision — host-reviewed or already corrected — failed the RPC's stale-revision check and could never be corrected; - the GET route dropped `publishedGames`, so refreshing after a correction emptied the editor for completed reports until a full browser reload; - the loader also dropped `hostSubmittedAt`. Both now go through a single mapMatchReportRow, so the shapes cannot drift apart again. The client no longer substitutes a revision it does not have: an absent revision means the list is stale, and it says so instead of sending a guess. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bs5rcUV4wybANpywoyaMAB
Closes the last gap from the match-report rework: a report that reached
donewas read-only, so a completed game's stats and screenshots could never be fixed.What changed
New correction API —
src/app/api/admin/match-reports/[id]/correct/route.tscalls thecorrect_match_report_resultRPC (shipped in sal-database db-v1.21.1). It requires a non-emptyplayerIdon every row and maps the RPC's error codes onto409so the client can distinguish a real conflict from a server fault:55000235052351423503Correction mode in the UI —
MatchReportClientgainsbeginCorrection(), which mints a freshcrypto.randomUUID()correction key and unlocks the editors for a published report. Submitting sendsexpectedRevision, so two admins correcting the same report concurrently cannot silently overwrite each other; the second one is told the report moved.Completed reports now render their real numbers —
src/lib/match-report-published.tsrebuilds the display fromplayer_match_statsrather thanextracted_data.extracted_dataholds the original AI extraction and never receives admin corrections, so a corrected report used to display its pre-correction values.Contract bump —
db-contract.lock.jsonmovesdb-v1.20.0→db-v1.21.1and vendors the matching generated types, which is what makescorrect_match_report_resulttypecheck.Why the pin is 4bf9f34e and not the contract-bump merge commit
contract.jsonandgenerated/database.types.tsare byte-identical at9990110a(the bump) and at4bf9f34e(current main) — the two commits in between changed seed data and tests only. sal-database's release workflow requires the released commit to be main's head, so4bf9f34eis the commit that can carry thedb-v1.21.1tag.Verification
npx tsc --noEmit— clean; theTS2345oncorrect_match_report_resultis resolvednpm run lint— 0 errors (10 warnings, all pre-existing on main)npx vitest run— 788 passed, including 7 new tests for the correction routesha256:4f4564…, matchingcontract.json'stypesSha256Known blocker — CI will be red until a tag is published
npm run check:db-contractfails with exactly one error:The verifier resolves the lockfile at both the pinned commit and the release tag, and requires the artifacts to be identical. The commit resolves fine;
db-v1.21.1has not been published yet. In this repo family, contract release tags are created by sal-database'sdeploy.yml(gh release create) after a production deploy — that workflow requirescommit_shato equal main's head and arestore_evidence_sha256attested independently from the private restore-evidence bundle, so publishing it is a human step by design.Once
db-v1.21.1exists at4bf9f34e, this check passes with no further code changes.Follow-up, deliberately not in this PR
The approval path (
resolve_match_report_review_unpublished) still has the cross-division roster gap that the correction path now closes. It deserves its own reviewed change rather than being folded in here.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bs5rcUV4wybANpywoyaMAB
Generated by Claude Code