Merge 2026 05 26#5
Merged
Merged
Conversation
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Fix /account/reset-password route * changeset * tidy * improve error reporting * messages * name all providers
* pds: setup entryway mock server for tests, remove stale dep. * changeset * tidy test * pds: tidy mock entryway
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Blur media on account level adult labels * WIP blur more * Update defs * Update tests * Changeset * Delete artifacts --------- Co-authored-by: Eric Bailey <git@esb.lol>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add AA config for BR * Changeset
* ✨ Add expiring tags in ozone * 🧹 Cleanup * 📝 Add changeset
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Add associatedRecord to external embed record
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* 📝 Initial draft of report based ticketing system * ✨ Introduce report entity * cleanup * ✨ Fix merge conflict * 🧹 Cleanup * ✨ Return reporter details in report view * 🐛 Remove foreign key ref * ❇️ Action reports via events (bluesky-social#4681) * ✨ Allow associating actions to reports via events * 🐛 Rollback subject type * ✨ Add queue management lexicons * ❇️ Ozone queue management (bluesky-social#4686) * ✨ Add queue management lexicons * :rotating_lights: Fix lint issue * 📝 Update lexicons * ❇️ Ozone queue routing (bluesky-social#4687) * ✨ Add queue routing for for reports * ✨ Add queue delete and migration * ✨ Add validation for subject type * 🧹 Cleanup * Ozone User Assignments (bluesky-social#4663) * moderator_assignment table * queue assignment endpoints * cleanup schema * report assignment endpoints * register handlers * allow queue unassign * rename tools.ozone.queue.assign to tools.ozone.queue.assignModerator * remove unique constraint since expired assignments arent removed * remove queue assignment report ID * Realtime assignments (bluesky-social#4682) * add ws types * basic web socket * add getAssignments to AssignmentService * add multiple mods to queue test case * add triage test cases * add realtime test case * add backfill logic * cleanup * websocket server restructure * keep assignment queue id if not provided * fix test: subscription receives updates * fix test: new subscription receives snapshot * add error handling * fix test count * ws: basic auth * report ended test * move assignment wss inside assignment service * allow realtime updates to be disabled * add assignment duration env vars * cleanup * default to 5 minutes * rename claimReport to assignModerator * add test to check dids parameter * remove subject param from queue.getAssignments * cleanup * only throw AlreadyAssigned if active assignment to another user is attempted * remove queue.assignModerator assign input * add more queue tests * tools.ozone.report.getAssignments * qualify function for clarity * cleanup wording * dont call broadcast within wss since the service handles it * throw error in either assign case * handle unassignment case properly * adjust assignment change test case * cleanup * cleanup * split getAssignments into 2 methods. update downstream endpoints * cleanup * support token query param * add queue:assign server message * attaach did to queue:assign message * refactor realtime tests to use websockets for updates * use http for creating queue assignments * cleanup * create view methods * cleanup * cleanup * remove assignment websocket server usage * delete assignment wss * remove line to keep diff clean * add db indexes * use varchar timestamps * remove redundant types * add pagination to queue.getAssignments * add pagination to report.getAssignments * hydrate queueView when reading/writing queue assignments * hydrate queue when working with report assignments * rename test * cleanup * remove queue service since it wont be needed * update description * (forgot to run codegen) * default to true * update description * cleanup * cleanup * use deterministic IDs * remove ws types * rename queueView to queue * create getAuthDid * create queueFromJoined to be able to reuse queueService.view * Ozone assignment refactors (bluesky-social#4707) * add invalid queue error * add InvalidAssignment error when assigning report * add invalid assignment tests * update report assignment tests to seed reports * create tools.ozone.report.unassignModerator * require DID when assigning to queue * only check queue collection match if routing a record * add description to report_queue * add description to queue API * add queue description to test * codegen * Ozone enable broad queue record matching (bluesky-social#4724) dont match collection if it is null * ♻️ Refactor report view lexicons and introduce single report query (bluesky-social#4727) * ♻️ Refactor report view lexicons and introduce single report query * 🧹 Cleanup * 🧹 Cleanup queueView * 🐛 Fix typo * 🐛 Fix remaining broken test * ✨ Enrich moderator team member for assignments (bluesky-social#4737) * ✨ Enrich moderator team member for assignments * 🧹 Cleanup * ✨ Return only permanent assignments * 🐛 Fix did assignment * Ozone report rerouting (bluesky-social#4738) * create stub endpoint * cleanup * lift queue router logic up into service layer * remove report routing tests from this file * test for regression bug * use assignReportBatch in routeReports * cleanup tests * create tools.ozone.queue.getLatest * rename to tools.ozone.report.getLatestReport * cleanup * fix limit bug * properly group OR clauses * require limit when assigning reports to queues for more robust limit enforcement * cleanup * update description * cleanup lint issues * allow mods to route reports * add not found error to single report queries * create reusable query for report service * hotfix: update to new function name * ❇️ Report activity and status change log (bluesky-social#4752) * ✨ Add report activity entity * ✨ Insert report activity when queueing and actioning * ✨ Only create report activity on permanent assignment * ✨ Move to object based activity schema * ✨ Adjust event types * ✨ Add member details to listActivities and cleanup note * 🧹 Revert lex-cbor package changes * ✨ Allow admins to assign mods to reports * 🐛 Fix issues pointed out in review * :rotating_lights: Fix lint issue * ♻️ Refactor report status update path * ❇️ Muted report support and better indexing/denormalizing of report data (bluesky-social#4812) * ✨ Add muted report support * ⚡ Improve indexing for reports and bring over subject message ref * 🐛 Fix open report action association * ✨ Add assignedTo filter param for queryReports * :rightwards_twisted_arrows: Untangle botched merge * 🐛 Fix client instantiation * ✨ Cleanup assignment normalization * Ozone report stats (bluesky-social#4789) * create report_stat table * create report stats service * create tools.ozone.queue.getLiveStats lexicon * implement endpoint * simplify service method * attach stats computation to daemon * add stats tests * implement queue stats * cleanup * fix index to only enforce uniqueness for live stats * switch to mode + timeframe schema * overhaul report stat service to use materialzation patterns * add force param. fix bugs * use -1 to make conflict detection easier * cleanup * fix update bug * simpliofy * add report table index for stats * cleanup * remove unused stats * use for loop to prevent hitting connection limit * log errors * change pending to be windowed * create StatsComputer * create isGroupFresh * cleanup * optimize computeGroup * create idx_report_stat_lookup * docs * cleanup * cleanup * actually start the stats computer :) * lint * clean up stats fields * lint * re-use viewing logic * destroy stats computer * add moderatorDid to report_stat * create stats for aggregate, queues, and moderator * add description * add avgHandlingTimeSec * create AHT test * allow assigned reports to be closed via reportAction * add computeStats * update test now that stats can be undefined * cleanup * refactor test * cleanup * fix bug * cleanup * create const for live stats ttl * rename fixed to historical * change pendingCount to be all time * change pendingCount to be all-time * put live stats ahead of historical when computing stats * fix * dont reference moderation_event for mod stats * add mod assignment index for stats * allow stats for unqueued reports by making queueId nullable * add test for unqueued report stats * cleanup * cleanup * cleanup * add report type stats * cleanup * fix failing report type tests * create single getLiveStats endpoint * cleanup * create getHistoricalStats * cleanup * make historical stats paginated * group report types * linting * cleanup * fix bug * ✨ Generate weekly stats, refactor, add locking * 🔨 Refactor live and historical stats to use day based snapshots * 🐛 Bring back datetime format check * ✅ Pass date string for test * stats: make computer interval configurable * cleanup --------- Co-authored-by: Foysal Ahamed <foysal@blueskyweb.xyz> * Ozone ticketing system fixes (bluesky-social#4884) * use defined report reason type * lint fix * Various fixes (bluesky-social#4882) * 🐛 Include actions in response in getReport response * ✨ Implement reassign queue endpoint * :rotating_lights: Fix lint issue * 🐛 Fix various issues found during testing queue system (bluesky-social#4910) * 🐛 Add status change on report unassignment * ✨ Add queue unassignment endpoint * ✨ Make queue assignments permanent by default * ⚡ Add better indexes, make status required * ✨ Add did filter param to queryReports * ⚡ 🔨 Move report creation to queue router (bluesky-social#4917) * ⚡ 🔨 Move report creation to queue router * fix tests for new required status and immediate report routing * cleanup --------- Co-authored-by: gcwill70 <gcwill70@gmail.com> * 🧹 Cleanup reviewedBy filter * 📝 Add changeset * :rotating_lights: Fix lint issue --------- Co-authored-by: Geoff Cramer <gcwill70@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix impossible comparison in service auth * add changeset --------- Co-authored-by: Matthieu Sieben <matthieu.sieben@gmail.com>
* update chat lexicons * codegen
* update chat lexicons * changeset
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add devEngines config for node and pnpm * Update GitHub Actions * Feedback
…edExternalView` (bluesky-social#4915) * Add rich metadata fields to app.bsky.embed.external#viewExternal * Hook up hydration * Update lexicons to match new changes * Hydrate site.standard records by ref on the read path Replaces the placeholder GetSiteStandardDocumentsWithPublication RPC with GetSiteStandardRecordsByURI and GetSiteStandardRecordsByRef to match what tango exposes. Hydration uses the ByRef variant so embedded link cards freeze to the exact record versions present at post time. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Plumb hydration state into externalEmbed view Threads HydrationState through externalEmbed call sites and adds a lookup helper that resolves associatedRecords against the hydrated site.standard documents and publications by their "<uri>@<cid>" keys. Field extraction is left as a follow-up. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Implement getEmbedExternalView for compose-time link cards Switches the lex query to take an array of site.standard.* URIs and return strongRefs alongside an optional viewExternal. The handler resolves URIs through GetSiteStandardRecordsByURI and emits strongRefs suitable for embedding into a post's external.associatedRecords. viewExternal field decoration and verification are left as follow-ups. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Update getEmbedExternalView lex, update code to use new lexicons * Integrate new protos and response shapes * Update lexicons * Hydrate labels for site.standard.* URIs and gate on takedown Appends site.standard record URIs to the post-hydration label fetch so state.labels carries entries for every SS doc/publication referenced by an embed. Adds actionSiteStandardTakedownLabels to drop hydrated SS records whose labels include a takedown from a redacting labeler. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Decorate viewExternal with site.standard records externalEmbed now layers a site.standard-derived overlay on top of the post's embed values, so hydrated document/publication fields override title/description/thumb/createdAt/updatedAt/labels/source while uri and associatedRefs always come from the post. The overlay is exposed as externalEmbedFromStandardSite for reuse. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Wire getEmbedExternalView through site.standard hydration Adds Hydrator.hydrateEmbedExternalViewFromUris which filters input URIs to site.standard.*, hydrates the latest record versions plus labels, and gates taken-down records. The handler runs that hydration, builds associatedRefs/Records from the surviving entries, and produces a view via Views.externalEmbedFromStandardSite. Adds a required `url` param to the lex so the returned viewExternal can carry the user's pasted share URL as its `uri`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Refactor getEmbedExternalView to createPipeline Adopts the skeleton/hydration/rules/presentation pattern used by other query handlers (searchPosts, getFeedGenerators) for parity. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * PR review nits - Clarify in the getEmbedExternalView lex that `view` is optional - Simplify the no-fields-set branch in buildExternalEmbedSourceTheme - Make externalEmbed's overlay spread order robust to future overlay fields by re-setting uri and associatedRefs after the spread Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Extract site.standard hydrator into hydration/external.ts Splits the SS record types, key helpers, AssociatedSiteStandardRecord type, and the two getSiteStandardRecords RPC wrappers out of feed.ts and the SITE_STANDARD_NSID_PREFIX const out of hydrator.ts into a new ExternalHydrator class. The Hydrator gains an `external` field mirroring how actor/feed/graph/label are mounted, and call sites move from this.feed.* to this.external.*. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * PR review nits - Refresh siteStandardRefsFromPosts JSDoc to reflect uri@cid map keys - Note in the getEmbedExternalView handler why we pass over the hydration maps twice - Move AssociatedSiteStandardRecord back to views/index.ts since it's internal to that module's view-building helpers Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Thread includeTakedowns through read-path SS hydration The compose-time hydrator method already forwards ctx.includeTakedowns; mirror it on the read path so parseRecord respects the same flag and takedown gating stays consistent across the two entry points. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Fix var shadowing * Update changeset * Update lex, update codepaths * Merge labels * Format * Remove comment * Prune publications not referenced by any hydrated document The dataplane auto-resolves a document's `site` field, so a request can return a publication the caller never asked for. If the caller also passed a publication URI that doesn't match any document's `site`, that publication is dropped to keep the response strictly tied to the documents we returned. Publication-only resolution (no documents hydrated) skips the prune. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Format * Estimate reading time from document.textContent Uses a coarse 200wpm word-count heuristic to populate viewExternal's readingTime when a document has plaintext content. Factored into a helper so swapping in a library later is a one-function change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move publication pruning into the getEmbedExternalView handler The hydrator method goes back to a generic "fetch what was requested" shape. Request-shape semantics — i.e. dropping publications the dataplane auto-resolved that the caller didn't actually ask for — now live in presentation, alongside the rest of the response assembly. collectAllowedPublicationUris is exported from hydration/external for reuse. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Extract site.standard helpers into util/standard-site.ts Moves SITE_STANDARD_NSID_PREFIX, the (uri, cid) key helpers, collectAllowedPublicationUris, AssociatedSiteStandardRecord, lookupAssociatedSiteStandardRecords, and estimateReadingTimeMinutes out of hydration/external and views/index into a single util module. The util keeps no hydration imports — collectAllowedPublicationUris and lookupAssociatedSiteStandardRecords are typed structurally over the record-info shapes they read. Also tightens the getEmbedExternalView handler: empty maps return early, publications get pruned only when documents are present, and the view is always returned with the request's `url` and overlay-derived title/description (falling back to empty strings) when any record was hydrated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Move SS hydration-state lookup back to hydration/external The util-level helper went generic over the doc/publication info shape, which lost the documented hydration-map types (and their `uri@cid` key docblocks) at the call boundary. Move it next to buildSiteStandardRecordsHydrationMaps under the more discoverable name getSiteStandardRecordsFromHydrationMaps and type it concretely against SiteStandardDocuments/Publications. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Mirror takedowns across site.standard doc/pub pairs Fold the per-record null sweep and the cross-pair sweep into a single actionSiteStandardTakedownLabels(documents, publications, labels) so neither read- nor compose-path callers can forget the second half. If either side of a doc/publication pair (discovered via doc.record.site) is taken down, mirror the null onto the other side; orphan docs and orphan publications still get the per-record sweep. Also: in the view layer, treat the document as authoritative for title/description as a unit — don't blend a doc's title with a publication's description. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Split externalEmbedFromStandardSite into ref- and record-based variants externalEmbedFromStandardSite stays as the read-path entry point, resolving doc/publication via the by-ref lookup against state, then delegates to externalEmbedFromStandardSiteRecords which owns the overlay-building body. The compose-path handler now captures the first hydrated doc/publication during its existing pass over the SS maps and calls the records variant directly, eliminating the redundant second walk. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Dispatch presentation by hydrated record type `presentation` is now a thin dispatcher that hands off to a `standardSitePresentation` helper when site.standard records were hydrated, returning `{}` otherwise. Future record types get their own branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Document empty-string contract on getEmbedExternalView output When the resolved records don't supply title/description, those fields land as empty strings on the returned view rather than the view being omitted. Spell that out in the lex description and at the build site so clients know to treat empty values as 'no enrichment' rather than 'titleless content.' Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Inline gate for publication allow-set Collapse the `prunePublications` flag into the allow-set itself: when no docs were hydrated, `allowedPublicationUris` is `undefined` and the publication loop allows everything; an empty set still means "constraint exists, nothing matches" and drops every publication. Removes the second variable that encoded the same rule. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Validate site.standard records against the assumed URL Both view-layer entry points now take an `assumedUrl` (the post's external.uri on the read path, the request's `url` on compose) and pass it through to validateStandardSiteForUrl. The validator mirrors the spec's rule tree: - Doc + publication: doc.site must resolve to the hydrated publication, and publication.url + document.path must equal assumedUrl. - Loose doc: doc.site must be HTTP and doc.site + document.path must equal assumedUrl. - Publication only: publication.url must equal assumedUrl. URL comparison normalizes via the WHATWG URL constructor (resolving path against base, lowercase host, dropping query/fragment, stripping trailing slash). On failure, the overlay is dropped and clients fall back to the bare embed. Adds parameterized tests covering the rule tree and the URL normalization cases. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Reject misconstructed doc/pub pairings during ref lookup When `associatedRefs` carries both a document and a publication, the hydration-map lookup now requires the doc's `site` field to equal the publication ref's URI; mismatches return both slots as `undefined` rather than silently rendering enrichment from records that don't agree. Doc-only and publication-only refs pass through unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Symmetric SS lookup contracts across read and compose paths Splits the SS hydration-map lookup into two named sister functions: - getSiteStandardRecordsFromHydrationMapsByRefs (read path) trusts the post's strongRefs and rejects pairs that disagree, including the case where a doc declares an at-uri `site` but no matching publication ref was pinned. - getSiteStandardRecordsFromHydrationMapsByDocumentUri (compose path) takes the first hydrated doc and pairs it with the publication its `site` field points at, rejecting the whole pairing when the declared publication wasn't auto-resolved. The handler now uses the compose-path variant directly, so the firstDoc/firstPub capture, the allowed-publications prune, and collectAllowedPublicationUris all go away. By the time externalEmbedFromStandardSiteRecords runs, both paths guarantee the same contract: doc+pub agree on URI, doc-only means loose doc, pub-only means publication-only flow, otherwise both slots are undefined. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Test the SS hydration-map lookups Covers both byRefs (read-path strict) and byDocumentUri (compose-path doc-first) variants, including the doc/pub pair-match invariant, the loose-doc and publication-only branches, the bail-out when a doc declares an at-uri publication that wasn't hydrated, and version-exact lookup misses. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Tighten validation and the empty-overlay contract - validateStandardSiteForUrl is now URL-match only; the structural pair-agreement and orphan-doc checks live exclusively in the hydration-map lookups (`hydration/external.ts`). Function and test comments cross-reference where each layer enforces what. - externalEmbedFromStandardSiteRecords now bails out and returns undefined when the chosen pair can't supply both `title` and `description`. The handler treats an undefined overlay the same as "nothing hydrated" and returns `{}` so Cardy falls back to its own card render and skips writing strongRefs to the post. - Lex description on getEmbedExternalView updated: empty-string fallbacks for title/description are gone; the response is empty when records are missing or validation fails. - Lookup JSDocs cross-reference each other: same observable contract, different upstream guarantee. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Drop unused collectAllowedPublicationUris The handler-side prune got obsoleted by the doc-first lookup (getSiteStandardRecordsFromHydrationMapsByDocumentUri), which only emits the publication a hydrated doc points at. No callers remain. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Remove untracked new codegen files * Remove untracked new codegen files for Ozone * Update comments * Refresh comments on the SS external-embed view methods The overlay is now a fully-populated `viewExternal['external']` (uri, title, description always set), not a partial. Update the JSDocs and the read-path spread comment to match: drop the "partial overlay" / "uri intentionally excluded" framing, explain the new return shape, and note that the spread on the read path layers SS-derived fields over post-author-supplied ones with `associatedRefs` re-set last because the post is authoritative. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Stick to convention * Fmt * Untrack generated files --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Improve bundling size of lex-client * Improve bundle size of oauth-client-browser-example
…luesky-social#4981) * Export `lexiconMessageSchema` (and type) for subscription messages * Remove need for inference when computing XRPC method derived types * tidy
…esky-social#4983) * Make the generation of the `$defs` namespace optional by default * remove un-necessary use of `$defs` accessor
…social#4984) * Add `default` export of "main" schema in the namespace file * update readme * fix table of contents
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* steering readmes toward lex client * fix lex package link in main readme * fixed CI Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * switch main readme links from npm to npmx * prettier pass --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add validation logging
* Fix subpath joining in validateStandardSiteForUrl
`new URL(path, base)` does relative-URL resolution, so a leading slash
on `path` swallows the base's pathname:
new URL('/indexing-standard-site', 'https://atproto.com/blog').href
// = 'https://atproto.com/indexing-standard-site' -- wrong
We want path-append semantics. Replace the `new URL(path, base)` call
with an explicit `joinPath(base, path)` helper that trims one slash
from each side and joins with exactly one `/`. The joined string is
still piped through `canonicalizeHttpUrl` for normalization.
Adds a parameterized test matrix covering all four slash placements ×
root-domain vs subpath bases, including the regression case from the
real-world atproto.com/blog publication that surfaced this bug.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Make fmt
* Changeset
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ed on external view only for SS hydrations (bluesky-social#5001) * Add some more logs * Build associatedProfiles inside externalEmbedFromStandardSiteRecords Hoists the doc/pub profile lookup into the shared SS overlay builder so both the read-path externalEmbed and the compose-path handler emit associatedProfiles from the same source of truth, derived from the selected document/publication refs rather than the post's full associatedRefs array. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Changeset * Format --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…es (bluesky-social#4992) * feat(did): add AtprotoDidRefAbsolute, DidRefRelative, AtprotoScopeAud, AtprotoTokenAud Renames AtprotoAudience to AtprotoDidRefAbsolute (kept as deprecated alias), adds DidRefRelative + isDidRefRelative for #fragment-only references, and introduces use-site unions: AtprotoScopeAud (always combined, for OAuth scopes) and AtprotoTokenAud (bare DID or combined, for service-auth JWT audience claims). Lays the groundwork for Phase 1 of proposal 0014. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(did): cover new DID-reference predicates Tightens isAtprotoDidRefAbsolute to reject empty fragments (`did:plc:abc#`), aligning it with isDidRefRelative which already rejects bare `#`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(oauth-scopes): use AtprotoScopeAud / isAtprotoDidRefAbsolute Switches from the legacy AtprotoAudience names to the renamed canonical forms. Behavior unchanged; the deprecated alias from @atproto/did keeps external consumers compiling. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(lexicons): loosen getServiceAuth.aud to accept did or did#serviceId The 'format: did' constraint excluded the combined did#serviceId syntax that OAuth-scoped credentials grant via 'rpc:lxm?aud=did#service'. Loosens to a plain string with a maxLength cap; handler-side validation will enforce that the value is either a bare atproto DID or an AtprotoDidRefAbsolute. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(lexicons): tighten getServiceAuth.aud description and maxLength Drops the internal "Phase 1" reference from the user-facing description and bumps maxLength from 1024 to 2048 to accommodate the spec maximum DID length plus a service-id fragment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(pds): accept did#serviceId aud in getServiceAuth handler Validates that the caller-supplied aud is a valid atproto DID or AtprotoDidRefAbsolute, then passes the original string straight through to permissions.assertRpc and createServiceJwt. Combined-aud OAuth scopes now match (resolves part of bluesky-social#4850); password-session callers continue to use bare DID. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(pds): pass combined did#serviceId aud to pipethrough scope check parseProxyInfo and parseProxyHeader now return the serviceId alongside the did and url. The proxyHandler uses the combined did#serviceId form when calling permissions.assertRpc, so OAuth credentials with 'rpc:lxm?aud=did#service' scopes succeed (resolves the proxy half of bluesky-social#4850). The outbound service-auth JWT keeps bare-DID aud — Slice D adds the operator-visible flag to flip that. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(pds): rename scopeAud/tokenAud to clarify Phase 1 asymmetry Names the Phase 1 audience asymmetry structurally rather than relying on a comment to document it. The scope check uses the combined did#serviceId form; the outbound JWT keeps bare-DID aud. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(pds): assert parseProxyHeader returns serviceId Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(oauth-scopes): cover combined-aud assertRpc match (issue bluesky-social#4850) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(pds): validate getServiceAuth aud for all caller types Moves the aud predicate check from the OAuth-only authorize callback into the shared handler body, so app-password and session callers also get syntactic validation — restoring the coverage that the lexicon's format: did constraint provided before Slice A loosened it to a plain string with maxLength. Also updates app-passwords tests to use valid atproto DIDs instead of did:example:test, which now correctly fails the predicate check. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Address review feedback for service-auth oauth fix - Annotate non-self-documenting checks in isAtprotoDidRefAbsolute and isDidRefRelative with terse case-name comments. - Spell out "Phase 1 of service auth updates" in the proxyHandler asymmetry comment. - Drop the issue-number reference from the assertRpc combined-aud describe block. - Mirror PR bluesky-social#4602's createReport pattern: use scopeAud/tokenAud naming to make the bare-DID JWT audience structurally clear. - Add a proxyHandler regression test that fails when scope-check aud reverts to bare DID. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(pds): use bare-DID aud in get/putPreferences proxy fallback When getPreferences/putPreferences proxy to a non-default appview, they were sending the combined did#serviceId form as the JWT audience. This contradicts the Phase 1 convention used elsewhere (proxyHandler, pipethrough(), createReport) where the scope check sees the combined form but the outbound service-auth JWT keeps bare-DID aud. Adds a small bareDidFromProxyTo() helper in pipethrough.ts and uses it at both call sites. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(pds): use real AppContext + override only authorization Replaces the proxy-oauth-aud test's stub-AppContext with a real TestNetworkNoAppView. Only AuthVerifier.authorization is overridden to drive the OAuth path off an x-test-scope header — every other ctx field stays real and real-typed. The override's signature is constrained by the real method type, so future shape changes (renames, output type restructures) break the test at compile time rather than silently diverging. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(pds): tighten proxy-oauth-aud cases Drops the lastAud capture (a 200 already implies combined-form aud reached the scope check) and replaces the bare-DID-aud negative case with a different-service-id negative case. Bare-DID aud isn't a valid rpc scope — isAudParam rejects it at parse time — so the prior negative case wasn't actually exercising audience matching. The new case (`upstream.did#atproto_other`) parses valid and proves the runtime aud's service id is being compared. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: add changesets for service-auth oauth fix Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(pds): cover getServiceAuth aud handling Adds five integration cases: bare-DID and combined did#serviceId aud both produce a token whose aud claim equals the input; malformed, non-atproto, and empty-fragment aud values are rejected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: pnpm style:fix Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(pds): revert scopeAud/tokenAud rename in handlers without both The naming pair was meant to disambiguate when scope-check and JWT audiences appear in the same scope. In get/putPreferences and createReport only one of the two is in scope at a time, so the rename adds noise without clarifying anything. proxyHandler keeps both names since they sit side-by-side there. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(did): drop OAuth-flavored types; remove deprecated AtprotoAudience Removes AtprotoScopeAud, AtprotoTokenAud, AtprotoAudience, and isAtprotoAudience from @atproto/did. The only consumer (oauth-scopes) now uses AtprotoDidRefAbsolute / isAtprotoDidRefAbsolute directly via its own AudParam alias. Keeps @atproto/did focused on DID-level primitives without OAuth or service-auth concepts. The AtprotoAudience/isAtprotoAudience deprecated aliases are dropped outright (breaking) — migration is a single import swap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(did): split did-ref.ts into atproto-agnostic primitives did-ref.ts now hosts generic DidRefAbsolute / DidRefRelative primitives (and their predicates) parameterized on a DID method. The atproto-flavored AtprotoDidRefAbsolute moves to atproto.ts alongside the other Atproto* helpers, defined as DidRefAbsolute<AtprotoIdentityDidMethods>. Public API surface is unchanged (both still flow through the package barrel). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(did): use String.includes for double-hash check Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: changeset for oauth-scopes breaking re-export change Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(oauth-scopes): tighten scopeNeededFor casts back to concrete types The casts `as AudParam`/`as LxmParam` widened to include `'*'`, which is wrong inside scopeNeededFor — it's constructing the specific scope for a single call, never a wildcard. Restore the original AtprotoDidRefAbsolute / Nsid casts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: lead BREAKING-change changesets with **BREAKING:** marker Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
The failing tests seems to be a timeout, I restarted it. |
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.
Merging from upstream Bluesky
Commit: 622d365
Date: Sun May 24 2026 05:51:02 GMT+0200 (Central European Summer Time)