Repository navigation
Replace appview reads with Constellation and direct PDS hydration - #75
Merged
Merged
Conversation
The web app no longer talks to the Skyboard appview. Board state is assembled in the browser from public atproto infrastructure: - Constellation (https://constellation.microcosm.blue) indexes every dev.skyboard.* record by its .boardUri field, so one target URI reveals which collections are non-empty and who participates in a board. - Each participant's PDS stays the source of truth for record content, read via com.atproto.repo.getRecord / listRecords. - Jetstream delivers live commits directly to the browser. Commit events carry the full record, so changes apply straight to Dexie instead of triggering a whole-board refetch. Writes are unchanged — clients already only wrote to their own PDS. src/lib/board-store.ts centralises the Dexie write rules both read paths share (local pending wins, unchanged rows aren't rewritten, one transaction per snapshot). It also prunes records that vanished upstream, scoped to the (did, collection) pairs that were listed successfully, so an unreachable PDS can't cause data loss. The appview never pruned, so other users' deletes used to linger locally forever. Records now arrive from arbitrary PDSes rather than through the appview, so src/lib/record-schemas.ts shape-checks them, mirroring the appview's zod schemas without adding zod to the browser bundle. Degradation is bounded: if Constellation is unreachable, discovery falls back to the board owner plus participants already known locally, and boards already in Dexie keep rendering and syncing. The appview server is left in place — cli/ still reads from it. Co-Authored-By: claudebot.disnetdev.com (did:plc:n6ku5xddiuguwze3f356evla) <claudebot.disnetdev.com@noreply.radial>
Co-Authored-By: codexbot.disnetdev.com (did:plc:hbonvqr5ysrscg5wdyb5klie) <codexbot.disnetdev.com@noreply.radial>
Co-Authored-By: codexbot.disnetdev.com (did:plc:hbonvqr5ysrscg5wdyb5klie) <codexbot.disnetdev.com@noreply.radial>
CLI board reads reported every failure as "Board not found." A 5xx from the owner's PDS, a transport error, a plc.directory outage, or a malformed response all collapsed to null in getRecord and again in fetchBoardFromPds, so fetchBoardData returned before its BoardReadError handling and mutating commands could not tell absence from a degraded read. getRecord now returns null only when the PDS reports the record, its repo, or the owning identity as missing (400 RecordNotFound/RepoNotFound, the reference PDS's "Could not find repo" InvalidRequest, 404, or a 404/410 from the DID directory). Resolution failures, transport errors, non-success responses, unreadable JSON, and responses without a record raise a descriptive BoardReadError, as does a board record that fails schema validation. resolvePds returns a three-way result so an identity that does not exist stays distinct from one that could not be looked up. Syntactically impossible record keys are answered locally, since getRecord reports them with the same generic InvalidRequest as a broken request and ralph's parseBoardRef probes with arbitrary strings. fetchBoardData no longer swallows unexpected errors into a null return, and sb now prints BoardReadError messages as a single red line with exit status 1 instead of an unhandled rejection. Adds 15 regression tests covering owner-board 404 vs 503 vs throw, unresolvable and nonexistent identities, malformed responses, and invalid board records. Co-Authored-By: claudebot.disnetdev.com (did:plc:n6ku5xddiuguwze3f356evla) <claudebot.disnetdev.com@noreply.radial>
Web client: - Track live Jetstream writes per record while a snapshot is in flight, so a snapshot whose listings predate an event no longer overwrites it, re-adds a deleted record, or prunes a freshly created one. - Apply Jetstream events strictly in order through a queue. - Connect Jetstream before the login catch-up and wait for the socket to open; if it opens late, reload all boards to cover the gap. - Follow the Dexie board list and add watchBoard() so boards created, joined or opened after login get live events. - Share participant listings across boards loaded together, taking every board's prune footprint before any listing starts. - Re-resolve and retry once when a read against a cached PDS endpoint fails, so a migrated account doesn't stay unreadable for a day. - Never return a partial Constellation DID list; retry a failed page once. - Hand throttle slots directly to waiters so the concurrency cap holds. - Percent-decode did:web segments (ports). CLI: - Skip untrusted participants whose repos cannot be read, with a warning, instead of failing the whole board. The owner, the current user and owner-trusted DIDs must still read completely. - Percent-decode did:web segments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DXc6YqRZ9fnPhWwkiD7DFi
Reads no longer list participants' whole repos. Both the web loader and the CLI ask Constellation's /links for every record on a board and read each with getRecord, so a load costs what the board holds rather than what its participants have written elsewhere. The web loader also skips refetching ops, trusts, approvals and reactions it already holds as synced, since Skyboard never rewrites those in place. Web: - Prune only rows the network confirmed gone (RecordNotFound, moved to another board, or absent from a successful fallback listing). Local rows Constellation no longer links are checked with getRecord, so index lag can't prune a live record. - Fall back to listing owner + known DIDs only when Constellation is unreachable or reports no links at all (an unindexed new board). - Return false for a board record that fails validation instead of reporting success with nothing to show. - Tell missing from failed PDS reads; only failures and missing repos evict the cached endpoint, and never within a minute of resolving it. - Watch exactly the boards in Dexie plus those being loaded, so leaving or deleting a board stops its events (they used to resurrect it). - Queue a follow-up resync when one is requested mid-resync instead of dropping it. - After a tab was hidden, reopen the socket from the last cursor instead of reloading every board. - Replay the first connection from a minute before it was requested, so loads needn't wait for it and a slow open no longer triggers a second full load; catch-up skips boards discovery just loaded, and discovery watches the boards it loads. - Debounce notification scans off the Jetstream event queue. CLI: - Classify records by the collection they were linked from, not $type. - Share in-flight DID resolutions across parallel reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QwVmcmFKxaooAybXQWa7Z
- Only treat a 404 as a missing record when it carries an XRPC error body. A bare 404 from a stale endpoint or proxy is now a failed read: the web client re-resolves the PDS and never prunes on it, and the CLI raises BoardReadError instead of reporting the record as absent. - Count every local Dexie delete as a touch for open snapshots, so a board load that was in flight when the user deleted a row can't re-add it. - In the CLI, require full reads from any participant with tasks on the board. Their tasks and own ops are materialized whether or not they're trusted, so skipping them hid cards and skewed `sb mv` positions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHoHenjVLSWUDFHPQaN8cJ
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.
Replaces Skyboard's custom appview read path with Constellation discovery, direct PDS hydration, and browser-side Jetstream updates.
Radial artifact
Delivered
getRecordreturns null only when the PDS reports the record, its repo, or the owning identity as missing, and raisesBoardReadErrorfor resolution failures, transport errors, non-success responses, unreadable bodies, and records that fail validation.sbprints those errors as one red line and exits 1.Verification
npm --prefix cli test(84 tests passed; 15 new regression tests for owner-board 404 vs 503 vs throw, unresolvable and nonexistent identities, malformed responses, invalid board records)npm --prefix cli run buildnpm run check(0 errors; 22 pre-existing warnings remain)npm run buildgit diff --checkRemaining risk
The two-browser live-edit and logged-out public-viewer UI flows still require a manual browser pass before deployment. Keep the legacy Fly appview alive during the rollout soak for already-open old web bundles; decommissioning remains a separate operational step.
The web client still collapses "board record unreadable" into "Board not found" on the board page — pre-existing appview-era behaviour, not covered by this change.
The repository-wide formatting check scans ignored
cli/distoutput after the CLI build and reports those generated files; the changed source and test files are clean.