release: v1.12.0#236
Merged
Merged
Conversation
…ding them (#225) (#231) crosspostToBluesky/Threads/DayOne return { success:false } on a transient failure (they never throw), but the compose path only handled the Bluesky success case and used a bare .catch() for Threads/DayOne — which only sees thrown errors. So a Bluesky/Threads/Day One blip at publish time silently lost the crosspost with no log line (an incident lost a video's Bluesky copy this way with zero evidence). - Bluesky: add an `else if (!success)` branch logging the returned error (matches publish-post.ts / the scheduled path). - Threads + DayOne: check the returned `success` in a .then() and log the error, keeping the .catch() for thrown errors. Makes the failures visible; automatic retry lands separately (#225 part 2). Test drives the create path with each crosspost failing. Co-authored-by: Samuel Lison <samuel@slcolonies.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) (#232) * feat(crosspost): persist + retry failed Bluesky/Threads crossposts (#225) Part 2 of #225 (the robustness fix, building on the logging in #231). A transient crosspost failure at compose time was logged but still lost — the crosspost was never re-attempted. - FailedCrosspost table (new; @unique(postId,platform) + nextRetryAt index) + manual migration. Stores the exact crosspost args (payload JSON) re-sent verbatim on retry. - compose/route.ts enqueues a Bluesky/Threads failure (Day One, a local journal export, logs only — no retry). - src/lib/crosspost-retry.ts retryFailedCrossposts(): claims each due row via an atomic compare-and-swap on nextRetryAt, re-attempts via crosspostToBluesky/crosspostReplyToBluesky/crosspostToThreads, writes the blueskyUri/threadsPostId marker + deletes on success, backoff (2m→10m→1h→6h→24h) then terminal after 6 attempts, prunes ONLY terminal rows by failedAt age (never a pending row — the #207 prune lesson). - Fourth scheduler job crosspostRetry (SCHEDULER_CROSSPOST_*, default on/60s) wired into the config overlay + the live /admin/settings toggle. Tests: 413 total (crosspost-retry: enqueue payload/idempotency, per-platform re-send + marker write, atomic claim, backoff steps + give-up boundary both sides, terminal-only prune; scheduler dispatch; compose enqueues on failure). Closes #225 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(crosspost): duplicate-guard the retry (no remote dedupe) + clean up on delete Adversarial review: unlike AP redelivery (deduped remotely by stable id), Bluesky/Threads don't dedupe a re-sent post — so a crash between a successful crosspost and its bookkeeping, or a landed-but-reported-false original, could publish a DUPLICATE public post. - retryFailedCrossposts now re-reads the Post before re-posting and SKIPS (deleting the row) if the platform marker is already set or the post is gone; a DB read error skips the tick (keeps the row) rather than risk a duplicate. - deletePostWithFederation also clears FailedCrosspost rows, so deleting a post can't leave a pending retry that publishes the removed content later. - Documented the irreducible residual (landed-but-reported-false → one possible duplicate; the platforms offer no client idempotency key). - Tests: marker-set skip, deleted-post skip, unverifiable-skip-keeps-row, claim writes a 5-min lease; delete-post asserts the FailedCrosspost cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Samuel Lison <samuel@slcolonies.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Patch bump, triage sandbox-verified + re-verified here (tsc clean, 416 tests, build clean). No code changes needed. Co-authored-by: Samuel Lison <samuel@slcolonies.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
prepare-release.mjs minor: '## Unreleased' → '## 1.12.0 (2026-07-12)', package.json + lock bumped. Ships the #225 crosspost silent-failure fix (log + persisted retry queue) + @atproto/api 0.20.27. Co-authored-by: Samuel Lison <samuel@slcolonies.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Release v1.12.0 — dev → main (merge commit). Ships the crosspost reliability work that soaked on
devsince v1.11.0.Highlights
GOAWAY) used to be discarded with no log line and no retry — a real incident lost a video post's Bluesky copy with zero evidence. Now every failure is logged, and Bluesky/Threads failures are persisted and retried by the scheduler with backoff (2m → 24h), writing theblueskyUri/threadsPostIdmarker on success. A re-read-before-repost guard prevents duplicate public posts (Bluesky/Threads don't dedupe like ActivityPub). New fourth toggle on/admin/settings.@atproto/api0.20.26 → 0.20.27.Schema (operators)
Additive only — new
FailedCrossposttable.npm run updateapplies the migration. No breaking changes.Full notes: the
## 1.12.0 (2026-07-12)section of CHANGELOG.md.