Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
Hosted PartyKit gives no persistent logs: `partykit tail` is a live websocket with no backfill, and the logpush/tailConsumers/analytics config keys all require your own CLOUDFLARE_ACCOUNT_ID + API token, which a *.partykit.dev deploy does not have. So troubleshooting a report that arrives hours after the fact needs the room to be able to describe its own health on demand. `GET /parties/main/<room>?debug` now returns a health snapshot. The default GET response is untouched — the debug branch is opt-in via the query param, so clients calling getPartykitState() are unaffected. The snapshot answers "did this room's persisted snapshot stop advancing?" in one request by comparing three views of the state: - memory: the live store's fingerprint - storage: read back from room storage, with matchesMemory - supabase: the persisted row plus its updated_at, with matchesMemory plus per-instance diagnostics: how it hydrated (source/when/fingerprint), storage write tallies (started/ok/failed/unsettled), supabase upsert tallies, last action, last error, and connection count. A `warnings` array and `healthy` boolean make it self-interpreting; notably it calls out storage and supabase disagreeing, since onStart prefers storage and a stale value there pins the room to an old checkpoint indefinitely. Caveat documented in the code: requesting this instantiates the room if it was evicted, so the counters describe the instance you just woke. The storage/supabase comparison is the part that survives an eviction. Verified against a local `partykit dev`: the plain GET still returns raw AppState unchanged; after driving two actions over a websocket the snapshot reports seq=2, storage matchesMemory=true, 2 started / 2 ok / 0 unsettled, healthy=true. Re-run with a deliberately stalled storage write reproduced the production shape and reported memory=346 vs storage=291, unsettled=1, healthy=false with both expected warnings. tsc --noemit, oxlint, and oxfmt pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmuaLNBXFwzHU6BUd1vGtD
When an organizer hits sync trouble mid-event, we currently have no way to
find out what their client saw. Server logs cover the room's side, but the
client's view — when it dropped, what it was still holding, what got
refused — was only in a devtools console nobody has open at a venue.
Adds a diagnostics panel reachable from the app header in event mode
(hamburger → "Connection diagnostics"), gated with EventModeGated so it
doesn't appear in classic mode. It shows:
- a plain-language connection log with local timestamps, covering
session start, connect/reconnect, disconnect, catch-up replays,
heartbeat loss, resyncs, blocked changes, rejected actions, and
actions abandoned after repeated attempts
- a live list of changes not yet saved to the server, with the action
type, how long each has been waiting, and how many send attempts
- a copy button that puts a report on the clipboard: room, timestamp
with UTC offset, user agent, pending list, then the whole log
- a prompt asking the user to post it in a thread on the Discord server
The log lives in `src/party/diagnostics.ts`, deliberately outside redux for
the same reason `connection-status.ts` is: anything in the store gets
broadcast to the room and persisted into every event's snapshot, and a
per-client debug log is neither shared state nor something to write there.
It is a 200-entry ring buffer read through useSyncExternalStore, so the
panel updates live while open.
`SyncManager` grows a `pendingActions` getter (and records when each
pending entry was first sent) so the panel can read the real pending list
rather than duplicating that state.
DISCORD_INVITE_URL in diagnostics-dialog.tsx is intentionally left empty: no
invite link exists in the repo, and the prompt renders fine without one
rather than shipping a URL that goes nowhere. Set it to make the link
appear.
Verified in a real browser against both dev servers, driving the actual
failure path with the workerd SIGSTOP recipe from the verify skill:
- while the server was frozen, the panel showed 2 pending event/addCab
entries with live ages and attempt counts
- after recovery the log read: action-abandoned x2, heartbeat-lost ("no
reply to 2 pings; forcing a reconnect"), disconnected, then
reconnected (seq 3)
- the copy button produced the full report on the clipboard and flipped
to "Copied!"
tsc --noemit, oxlint, and oxfmt pass; webpack builds clean.
New UI strings are in en and ja; the Japanese is a best effort and should
get a native-speaker pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmuaLNBXFwzHU6BUd1vGtD
The invite already existed in the about/credits dialog; it just wasn't found earlier because a shortened discord.gg URL doesn't contain the word "discord" in any searchable form beyond the host. Rather than repeat the URL in a second place where the two could drift, this extracts it to src/external-links.ts and has both the about dialog and the diagnostics panel read from there. The diagnostics prompt now always renders the link, so the empty-string fallback branch is gone. Verified in a browser: the diagnostics panel renders "Open Discord" -> https://discord.gg/QPyEATsbP7 with target=_blank, and the about dialog still carries the same link after the refactor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmuaLNBXFwzHU6BUd1vGtD
The prompt next to the copy button asks people to paste the report into a thread on Discord, so format it for that destination: - bold labels on the header fields, so the room/time/browser are skimmable rather than a wall of text - the pending list and the connection log go in fenced code blocks, which keeps their column alignment in Discord's proportional font and — more importantly — stops markdown from eating their contents. Action types and room names routinely contain underscores, and a reducer's error message can contain anything; outside a fence `foo_bar_baz` renders as partly italic. Any backticks in that content are neutralised so they can't close the fence early. - the event column is padded to a common width so details line up - empty pending/log sections render as italic placeholders instead of an empty code block Also fixes the UTC offset in the header, which divided minutes by 60 without flooring: a half-hour zone came out as "UTC+5.5". It now formats as +05:30, verified across UTC, +02, -05, +05:30, -03:30 and +14. Verified by driving the browser with the server frozen so the report had both pending changes and failure events, and by exercising the formatter directly for the empty case, the common no-pending case, and a hostile case carrying triple backticks, underscores and asterisks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmuaLNBXFwzHU6BUd1vGtD
A 200-entry buffer produced a ~21k character report, well past Discord's
2000 character message limit. Discord turns an over-long paste into a
message.txt attachment, which works but throws away the markdown the last
commit added — and the user has no idea that happened.
The main copy button now trims to fit. It keeps the most recent events,
since the tail of a log is where the trouble is, and says so in the report
rather than silently dropping them:
_171 earlier events trimmed to fit — ask for the full log if needed_
Trimming is by character budget rather than a fixed event count, because
detail lengths vary a lot; it includes as many recent events as fit under
the limit (with a little headroom). The header reflects it too: "most
recent 29 of 200 events" instead of "200 events". If even a single event
would overflow — a pathological pending list, say — it keeps that one event
rather than emitting a report with no log at all.
Adds a second "Copy full log" button for when you want everything. That one
is plain text, no markdown: it's expected to arrive as a file attachment,
where bold markers and code fences are just noise. Both formats come from
one builder so they can't drift apart.
The clipboard fallback textarea is now written imperatively via a ref, since
one hidden element has to serve both formats and a state update wouldn't
have landed before the selection.
Verified against the formatter directly: a 200-entry buffer trims to 1811
characters, keeps event 199, drops event 0, carries the trim note, and the
full report still contains every event across 20950 characters of plain
text; a 5-event log is untouched and carries no note. Then in a browser
with the server frozen: both buttons present and labelled, the trimmed one
yields markdown with fences, the full one yields plain text, and each shows
its own "Copied!" state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmuaLNBXFwzHU6BUd1vGtD
Diagnostics for the stuck-room bug: party server logging, a room log watcher, and a ?debug health endpoint
Add button to delete text sources
Prototype: custom edit lists in SMX data for alpha builds
Two small fixes found while merging this branch elsewhere. song-search/index.tsx used a literal NUL byte as the separator in chartIdentity's `.join()`. Git detects binary by looking for a NUL in the first 8000 bytes, so the whole file counted as binary: `git diff` showed only "Binary files differ" and a merge touching it refused to combine the two sides, silently keeping one. Writing the separator as "\0" is the same character at runtime and keeps the file diffable and mergeable. turnstile.tsx assigned onTokenRef.current during render, which trips the repo's own react-hooks-js(refs) rule, so `yarn validate:lint` fails on this branch. Moved into an effect keyed on the callback — writing a ref while rendering is also unsafe under concurrent rendering. https://claude.ai/code/session_01Duin5Bjnq1D6V3XzXx5qiW
Both repos merged and deployed 2026-08-10 (rgt-data PR #2 to main, next.ddr.tools PR #620 to partykit). Updates milestone/verification status, corrects stale "authoring SPA deferred" language (it was backed out, not deferred), and records the Turnstile/i18n decisions made while finishing the M1 UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # package.json # src/draw-state/store.ts # src/song-card/song-card.tsx # src/song-search/index.tsx # yarn.lock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This will be the future form of networking in the app. The whole app uses a shared state that is sync'd between as many users as you see fit. Multiple configuration presets are supported, and users/matches can even be pulled/pushed from a start.gg event. For a more guided tour of all the new features here, see this somewhat outdated video walk-through: https://youtu.be/4Gpj9jTNcfM
The main thing keeping this from merging and replacing our current main branch is the lack of some features that many people still expect, like local-only ITG imports. This can be solved in party by supporting these only in the "classic mode" (no networking) and eventually supporting some sort of external hosting for custom imports.
This PR is perpetually deployed at next.ddr.tools
Current known blockers