Skip to content

Fix Hashnode syndication recovery - #5520

Merged
shai-almog merged 1 commit into
masterfrom
fix-syndication-recovery
Aug 4, 2026
Merged

Fix Hashnode syndication recovery#5520
shai-almog merged 1 commit into
masterfrom
fix-syndication-recovery

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

What changed

  • Stop persisting a Hashnode draft URL as successful syndication when the publish dialog fails.
  • Add an explicit --post-slug recovery path for a known unpublished draft without changing the normal one-post rotation.
  • Distinguish public Hashnode publication from a retained draft in the runner output.
  • Add regression coverage for failed-draft persistence and explicit recovery selection.

Why

The browser flow could fail while setting tags, canonical URL, or clicking Publish, then record the retained draft URL in syndication-state.json. Candidate selection treated any URL as complete, so the post was never retried and the health checker could not see the missing public publication.

Validation

  • python3 scripts/website/test_syndicate_browser_posts.py
  • python3 scripts/website/test_syndication_health.py
  • python3 scripts/website/test_syndicate_blog_posts.py
  • python3 -m py_compile scripts/website/syndicate_browser_posts.py scripts/website/check_syndication_health.py scripts/website/test_syndicate_browser_posts.py
  • Parsed both syndication JSON files with jq empty.

Copilot AI lite review requested due to automatic review settings August 4, 2026 21:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0002945bf

ℹ️ 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".

entry = post.get(platform, {})
return bool(
isinstance(entry, dict)
and entry.get("published") is False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry legacy draft entries without published flag

When recovering an older failed Hashnode run, this predicate still returns false for draft-only records that predate the new published: false field. For example, scripts/website/syndication-state.json already has metal-and-skins with a https://hashnode.com/draft/... URL and no published key, so --post-slug metal-and-skins --platforms hashnode will hit state.is_syndicated(...), fail this recovery check, and skip the very draft the new recovery path is meant to unblock. Treating draft URLs, or missing published on a draft URL, as retryable would cover those legacy records.

Useful? React with 👍 / 👎.

if not retry_failed_draft:
print(f" [{adapter.name}] already syndicated; skipping.")
continue
print(f" [{adapter.name}] retrying explicitly recorded unpublished draft.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Open the recorded Hashnode draft on recovery

This branch says it is retrying the recorded unpublished draft, but the selected draft URL is never passed to HashnodeAdapter; submit_draft() still starts from the dashboard and clicks Write, which edits whichever current draft Hashnode opens. In a real recovery after later browser runs have left a different draft in that slot, --post-slug can overwrite or publish that other draft instead of the selected post, so the recovery path should navigate to and verify the stored draft_url before editing.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens the Hashnode browser-syndication flow so that a retained draft (when the publish dialog fails) is no longer persisted as a “successful” syndication, and adds an explicit recovery mode to reprocess a previously recorded unpublished draft without altering the normal candidate rotation.

Changes:

  • Introduces _completed_hashnode_result() to only persist Hashnode state after a public publish succeeds (otherwise fails the adapter run).
  • Adds --post-slug to target a single eligible post, with special-case logic to allow retrying a previously persisted unpublished Hashnode draft.
  • Adds regression tests for “unpublished draft is not treated as completed” and for the explicit failed-draft retry predicate.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
scripts/website/syndicate_browser_posts.py Stops persisting unpublished Hashnode drafts as completed, adds --post-slug selection + retry handling, and improves runner output messaging.
scripts/website/test_syndicate_browser_posts.py Adds unit tests for Hashnode completion behavior and the failed-draft recovery predicate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +138 to +146
post = state.raw.get("posts", {}).get(slug, {})
if not isinstance(post, dict):
return False
entry = post.get(platform, {})
return bool(
isinstance(entry, dict)
and entry.get("published") is False
and entry.get("draft_url")
)
Comment on lines +1243 to +1247
print(
f"Post {candidate.slug} is not eligible on {today.isoformat()} "
f"with a {args.min_age_days}-day delay.",
file=sys.stderr,
)
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog
shai-almog merged commit 44e6e06 into master Aug 4, 2026
12 checks passed
@shai-almog
shai-almog deleted the fix-syndication-recovery branch August 4, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants