skills/bluesky-engagement/SKILL.md
Automated Bluesky reply monitoring and draft queueing for ThumbGate's acquisition engagement loop. Polls the AT Protocol notifications endpoint every 15 minutes, writes human-reviewable draft replies into a queue file, and never auto-posts without sign-off. Trigger when the user asks about Bluesky replies, engagement, reply monitoring, or wants to see/approve queued drafts. Also the authoritative reference for rotating the Bluesky app password or diagnosing monitor failures.
npx skillsauth add igorganapolsky/rlhf-feedback-loop bluesky-engagementInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Automation for Bluesky reply tracking. The monitor reads; a human (or a queue consumer with explicit approval) writes.
Bluesky is a Zernio-connected channel for publishing, but Zernio does not expose an inbound/comments API. Engagement has to run directly against the open AT Protocol. This skill owns that path so future sessions don't re-ask for credentials or re-derive the PDS routing.
Both are set in .env at repo root (git-ignored):
| Var | Source | Notes |
|---|---|---|
| BLUESKY_HANDLE | bsky profile URL | e.g. iganapolsky.bsky.social |
| BLUESKY_APP_PASSWORD | https://bsky.app/settings/app-passwords | Scoped, revocable. Never use the account login password. |
Rotation: revoke the old app password at the settings URL above, generate a new one named thumbgate-replies, and replace the value on the BLUESKY_APP_PASSWORD= line of .env. No other files need updating.
launchd (com.thumbgate.bluesky-reply-monitor)
└─ every 900s ─> node scripts/social-reply-monitor-bluesky.js
├─ com.atproto.server.createSession (bsky.social)
│ └─ reads didDoc to find user's real PDS
├─ app.bsky.notification.listNotifications (user's PDS)
├─ filter reason in {reply, mention, quote}
├─ dedupe against .thumbgate/reply-monitor-state.json
├─ generateReply() — shared with Reddit/X monitor
└─ append to .thumbgate/reply-drafts.jsonl
Federation note: authenticated AT Protocol calls must hit the user's own PDS (session.didDoc.service[].serviceEndpoint), not bsky.social. Hitting bsky.social returns 502 UpstreamFailure. This was the first bug.
Transient failures: Bluesky's appview returns 502 during incidents. The monitor detects 5xx / UpstreamFailure / ECONNRESET and exits 0 so launchd doesn't mark the agent failed.
| Path | Purpose | Tracked? |
|---|---|---|
| scripts/social-reply-monitor-bluesky.js | The monitor itself | yes |
| ~/Library/LaunchAgents/com.thumbgate.bluesky-reply-monitor.plist | 15-min schedule | no (user-scope) |
| .thumbgate/reply-monitor-state.json | Dedupe state (also used by Reddit/X monitor) | no |
| .thumbgate/reply-drafts.jsonl | Human-review queue | no |
| .thumbgate/bluesky-monitor-stdout.log | launchd stdout | no |
| .thumbgate/bluesky-monitor-stderr.log | launchd stderr (transient 502 warnings land here) | no |
# One-off dry run (no state mutation, logs what would be queued)
node scripts/social-reply-monitor-bluesky.js --dry-run
# One-off real run (appends to .thumbgate/reply-drafts.jsonl)
node scripts/social-reply-monitor-bluesky.js
# Reload the launchd agent after editing the plist
launchctl unload ~/Library/LaunchAgents/com.thumbgate.bluesky-reply-monitor.plist
launchctl load ~/Library/LaunchAgents/com.thumbgate.bluesky-reply-monitor.plist
# Check the agent is alive
launchctl list com.thumbgate.bluesky-reply-monitor
# Tail the logs
tail -f .thumbgate/bluesky-monitor-stderr.log
# Inspect queued drafts
cat .thumbgate/reply-drafts.jsonl | jq -r 'select(.platform=="bluesky") | "\(.createdAt) @\(.notification.authorHandle): \(.incomingText[0:80])"'
The monitor NEVER auto-posts. Drafts sit in .thumbgate/reply-drafts.jsonl with autoPost: false. Human workflow:
send-reply-queue.js consumer that hits com.atproto.repo.createRecord with app.bsky.feed.post — keep the root/parent CID+URI pair the monitor already stored).Auto-sending is deliberately deferred until there's a UI approval step. This is how we stay off the bot-slop/banned list.
| Symptom | Likely cause | Fix |
|---|---|---|
| Bluesky auth failed (status=401) | app password revoked or rotated elsewhere | regenerate per Rotation steps above |
| listNotifications failed on bsky.social: 502 | hitting the wrong host | the script auto-routes to PDS; if you see this, the didDoc parsing broke — inspect session.didDoc.service |
| listNotifications failed on <user-pds>: 502 UpstreamFailure | Bluesky appview incident | nothing to do; soft-fails, next tick retries |
| stderr log grows but no drafts appear | all notifications already in state file or all reasons are like/follow (we only handle reply/mention/quote) | inspect state file: jq '.repliedTo.bluesky' .thumbgate/reply-monitor-state.json |
As of 2026-04-21 this monitor also runs hourly in GitHub Actions via .github/workflows/ralph-loop.yml → scripts/ralph-loop.js → step id reply-monitor-bluesky. The step is gated on requiredEnvAll: ['BLUESKY_HANDLE', 'BLUESKY_APP_PASSWORD'] (GitHub repo secrets) and writes the same draft file the local launchd agent does. Never auto-posts in either path.
First autonomous-posting attempt (scripts/bluesky-send-replies.js, removed 2026-04-21) was reverted after the CEO thumbs-downed the AI-pitch tone on a live reply. All 6 live replies were deleted via scripts/bluesky-delete-replies.js (calls com.atproto.repo.deleteRecord). The lesson, captured as memory mem_1776790570289_oc2z6g: do not write replies that open with "Exactly"/"Right —", do not name-drop ThumbGate features inside conversational replies, keep replies to 1–2 sentences in the voice of a human peer. Until this is enforced by a gate rule, the monitor stays draft-only and a human sends the actual reply.
scripts/social-reply-monitor.js — Reddit, X, LinkedIn monitor. Shares generateReply() and the draft file.scripts/bluesky-list-actionable.js — one-shot dump of un-replied notifications for human triage.scripts/bluesky-delete-replies.js — one-shot rollback; reads postedUri entries out of .thumbgate/reply-monitor-state.json and calls com.atproto.repo.deleteRecord.scripts/social-analytics/publishers/zernio.js — publishes Bluesky posts via Zernio. Separate concern.CLAUDE.md → "Social stack: Zernio canonical" — explains why publishing uses Zernio but engagement doesn't (Zernio Inbox is dashboard-only, no public API as of 2026-04-21).tools
List the active ThumbGate prevention rules, reliability rules, and the promoted lessons behind them, so the user can see which guardrails are currently protecting this project and WHY each one exists. Reads the live rule and lesson stores via the prevention_rules, get_reliability_rules, and search_lessons MCP tools (CLI fallback `npx thumbgate rules`). Use when the user says "what is ThumbGate protecting me from", "show my rules", "show my gates", "what has the agent learned", "list active guardrails", or "what's blocked here". Do NOT use to CREATE a new rule (use the thumbgate-guard skill), to see runtime enforcement counts of what actually fired (use the thumbgate-blocked skill), or to diagnose whether ThumbGate is wired up at all (use the thumbgate-doctor skill).
tools
Inspect this repo's branch and release governance (protected branches, release rules, protected-file globs) and, only when the user explicitly approves, grant a scoped, time-limited exception so a protected-file edit or publish can proceed under audit. Reads posture via the get_branch_governance MCP tool and records a narrow, expiring approval via the approve_protected_action MCP tool. Use when the user says "is main protected", "show branch governance", "what am I blocked from editing", "approve this protected change", or "let me edit a protected file just this once". Do NOT use to disable protection wholesale, to grant broad or standing exceptions, or to diagnose hook wiring (use the thumbgate-doctor skill) — this skill is for narrow, temporary, audited approvals only.
tools
Turn the agent's most recent mistake into an enforced ThumbGate prevention rule (a PreToolUse block gate) so the same bad tool call is intercepted before it runs again, in this and every future session across Claude Code, Cursor, Codex, Gemini, Amp, and Cline. Captures the failure with the capture_feedback MCP tool, then force-promotes it via `npx thumbgate force-gate` so it is enforced, not just logged. Use when the user says "guard against this", "block this from happening again", "never do that again", "make that a rule", "stop the agent from repeating that", or right after a bad action or thumbs-down that should become a hard rule. Do NOT use to merely log a thumbs-up/down without enforcement (use the thumbgate-feedback skill), to recall prior context before starting work (use the Agent Memory skill), or to list rules that already exist (use the thumbgate-rules skill).
tools
Health-check whether ThumbGate is actually wired into this agent — PreToolUse/SessionStart hooks installed, MCP server reachable, lesson store present, statusline, and overall agent-readiness — then report exactly what to fix. Runs the existing `npx thumbgate doctor` audit and the check_operational_integrity MCP tool. Use when the user says "is ThumbGate wired up", "thumbgate doctor", "check my guardrails are installed", "why aren't my gates firing", "is the MCP server connected", or "agent readiness". Do NOT use to view rules (use the thumbgate-rules skill), to view what was blocked (use the thumbgate-blocked skill), or to capture a new rule (use the thumbgate-guard skill) — this skill only diagnoses setup and wiring.